Completed
Push — master ( 126036...3fa63d )
by Maik
07:56
created

OrmAnnotation   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 359
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 2

Test Coverage

Coverage 91.54%

Importance

Changes 37
Bugs 3 Features 4
Metric Value
wmc 45
c 37
b 3
f 4
lcom 2
cbo 2
dl 0
loc 359
ccs 119
cts 130
cp 0.9154
rs 8.3673

14 Methods

Rating   Name   Duplication   Size   Complexity  
A getAnnotatedTableName() 0 17 3
A getAnnotatedPrimaryKeyProperty() 0 17 4
B getAnnotatedPrimaryKeyColumn() 0 19 5
A getAnnotatedPropertyType() 0 13 2
C getAnnotatedPropertyValue() 0 26 7
B getAnnotatedColumnValuePairs() 0 30 6
B getAnnotatedPrimaryKey() 0 32 6
B getAnnotatedType() 0 23 4
A getAnnotatedColumnFromProperty() 0 5 1
A getAnnotatedColumn() 0 10 2
A isIdAnnotated() 0 4 1
A isCascadeAnnotated() 0 4 1
A getAnnotatedMappedByParameters() 0 10 2
A isEager() 0 5 1

How to fix   Complexity   

Complex Class

Complex classes like OrmAnnotation often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use OrmAnnotation, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace Nkey\Caribu\Orm;
3
4
/**
5
 * Annotation provider for Caribu Orm
6
 *
7
 * This class is part of Caribu package
8
 *
9
 * @author Maik Greubel <[email protected]>
10
 */
11
trait OrmAnnotation
12
{
13
    use OrmDataTypeConverter;
14
15
    /**
16
     * Retrieve the annotated table name
17
     *
18
     * @param string $class The name of class
19
     * @param string $fallback As fallback if nothing was found
20
     *
21
     * @return string The name of table
22
     *
23
     * @throws OrmException
24
     */
25 32
    private static function getAnnotatedTableName($class, $fallback)
26
    {
27
        try {
28 32
            $rfClass = new \ReflectionClass($class);
29
30 32
            $docComments = $rfClass->getDocComment();
31
32 32
            $matches = array();
33 32
            if (preg_match('/@table (\w+)/', $docComments, $matches)) {
34 26
                $fallback = $matches[1];
35 26
            }
36
37 32
            return $fallback;
38
        } catch (\ReflectionException $exception) {
39
            throw OrmException::fromPrevious($exception);
40
        }
41
    }
42
43
    /**
44
     * Get the annotated primary key property name
45
     *
46
     * The property is annotated with the @id annotation
47
     *
48
     * @param string $class The name of class to retrieve the primary key property
49
     *
50
     * @return string The name of property which represents the primary key
51
     *
52
     * @throws OrmException
53
     */
54 10
    private static function getAnnotatedPrimaryKeyProperty($class)
55
    {
56
        try {
57 10
            $propertyName = null;
58
59 10
            foreach (self::getClassProperties($class) as $property) {
60 10
                if (self::isIdAnnotated($property->getDocComment())) {
61 7
                    $propertyName = $property->getName();
62 7
                    break;
63
                }
64 10
            }
65
66 10
            return $propertyName;
67
        } catch (\ReflectionException $exception) {
68
            throw OrmException::fromPrevious($exception);
69
        }
70
    }
71
72
    /**
73
     * Get the annotated primary key
74
     *
75
     * The property is annotated with the @id annotation
76
     * The propery may have a @column annotation to modify the database column name
77
     *
78
     * @param string $class The name of class to retrieve the primary key column of
79
     *
80
     * @return string|null The name of primary key column
81
     *
82
     * @throws OrmException
83
     */
84 20
    private static function getAnnotatedPrimaryKeyColumn($class)
85
    {
86
        try {
87 20
            $columnName = null;
88
89 20
            foreach (self::getClassProperties($class) as $property) {
90 20
                $docComment = $property->getDocComment();
91 20
                if (self::isIdAnnotated($docComment) &&
92 20
                    null === ($columnName = self::getAnnotatedColumn($docComment))) {
93
                    $columnName = $property->getName();
94
                    break;
95
                }
96 20
            }
97
98 20
            return $columnName;
99
        } catch (\ReflectionException $exception) {
100
            throw OrmException::fromPrevious($exception);
101
        }
102
    }
103
104
    /**
105
     * Get the property type via annotation
106
     *
107
     * @param string $class The name of class to retrieve a particular property type
108
     * @param string $propertyName The name of property to retrieve the type of
109
     * @param string $namespace The namespace
110
     *
111
     * @return string|null The property type either as primitive type or full qualified class
112
     */
113 26
    private static function getAnnotatedPropertyType($class, $propertyName, $namespace)
114
    {
115 26
        $type = null;
116
117 26
        $rfClass = new \ReflectionClass(self::fullQualifiedName($namespace, $class));
118
119 26
        if ($rfClass->hasProperty($propertyName)) {
120 26
            $property = $rfClass->getProperty($propertyName);
121 26
            $type = self::getAnnotatedType($property->getDocComment(), $rfClass->getNamespaceName());
122 26
        }
123
124 26
        return $type;
125
    }
126
127
    /**
128
     * Get the value from property
129
     *
130
     * @param object $from The source object
131
     * @param string $toClass The type of destination class
132
     * @param \ReflectionProperty $property The property to get value of
133
     * @param string $namespace The namespace of destination class
134
     *
135
     * @return array The type and value from property
136
     */
137 26
    private static function getAnnotatedPropertyValue($from, $toClass, \ReflectionProperty $property, $namespace)
138
    {
139 26
        $value = $property->getValue($from);
140
141 26
        $type = self::getAnnotatedPropertyType($toClass, $property->getName(), $namespace);
142
143 26
        if (null === $type || self::isPrimitive($type) || !class_exists($type)) {
144 26
            return array($type, $value);
145
        }
146
147 14
        $rfPropertyType = new \ReflectionClass($type);
148
149 14
        if ($rfPropertyType->getParentClass() &&
150 5
            strcmp($rfPropertyType->getParentClass()->name, 'Nkey\Caribu\Model\AbstractModel') == 0
151 14
            ) {
152 5
            $getById = new \ReflectionMethod($type, "get");
153 5
            $value = $getById->invoke(null, $value);
154
155 5
            return array($type, $value);
156
        }
157
158 14
        $value = $rfPropertyType->isInternal() ?
159 14
            self::convertType($type, $value) : $rfPropertyType->newInstance($value);
160
161 14
        return array($type, $value);
162
    }
163
164
    /**
165
     * Retrieve list of columns and its corresponding pairs
166
     *
167
     * @param string $class The name of class to retrieve all column-value pairs of
168
     * @param \Nkey\Caribu\Model\AbstractModel $object The entity to get the column-value pairs of
169
     *
170
     * @return array List of column => value pairs
171
     *
172
     * @throws OrmException
173
     */
174 12
    private static function getAnnotatedColumnValuePairs($class, $object)
175
    {
176 12
        $pairs = array();
177
        try {
178 12
            $rfClass = new \ReflectionClass($class);
179
180 12
            foreach ($rfClass->getProperties() as $property) {
181 12
                $docComments = $property->getDocComment();
182
183
                // mapped by entries have no corresponding table column, so we skip it here
184 12
                if (preg_match('/@mappedBy/i', $docComments)) {
185 2
                    continue;
186
                }
187 12
                if (null === ($column = self::getAnnotatedColumn($docComments))) {
188 10
                    $column = $property->getName();
189 10
                }
190
191 12
                $rfMethod = new \ReflectionMethod($class, sprintf("get%s", ucfirst($property->getName())));
192
193 12
                $value = $rfMethod->invoke($object);
194 12
                if (null != $value) {
195 12
                    $pairs[$column] = $value;
196 12
                }
197 12
            }
198 12
        } catch (\ReflectionException $exception) {
199
            throw OrmException::fromPrevious($exception);
200
        }
201
202 12
        return $pairs;
203
    }
204
205
    /**
206
     * Retrieve the primary key name and value using annotation
207
     *
208
     * @param string $class The name of class to retrieve the primary key name and value
209
     * @param \Nkey\Caribu\Model\AbstractModel $object The entity to retrieve the pimary key value
210
     * @param boolean $onlyValue Whether to retrieve only the value instead of name and value
211
     *
212
     * @return array The "name" => "value" of primary key or only the value (depending on $onlyValue)
213
     *
214
     * @throws OrmException
215
     */
216 16
    private static function getAnnotatedPrimaryKey($class, $object, $onlyValue = false)
217
    {
218
        try {
219 16
            $rfClass = new \ReflectionClass($class);
220
221 16
            foreach ($rfClass->getProperties() as $property) {
222 16
                $docComment = $property->getDocComment();
223
224 16
                if (!self::isIdAnnotated($docComment)) {
225 4
                    continue;
226
                }
227
228 12
                $rfMethod = new \ReflectionMethod($class, sprintf("get%s", ucfirst($property->getName())));
229
230 12
                if (null === ($columnName = self::getAnnotatedColumn($docComment))) {
231 1
                    $columnName = $property->getName();
232 1
                }
233
234 12
                $primaryKey = $rfMethod->invoke($object);
235
236 12
                if (!$onlyValue) {
237
                    $primaryKey = array(
238
                        $columnName => $primaryKey
239 12
                    );
240 12
                }
241 12
                return $primaryKey;
242 4
            }
243 4
            return null;
244
        } catch (\ReflectionException $exception) {
245
            throw OrmException::fromPrevious($exception);
246
        }
247
    }
248
249
    /**
250
     * Get the annotated type
251
     *
252
     * @param string $comment The document comment string which may contain the @var annotation
253
     * @param string $namespace Optional namespace where class is part of
254
     *
255
     * @return string The parsed type
256
     *
257
     * @throws OrmException
258
     */
259 31
    private static function getAnnotatedType($comment, $namespace = null)
260
    {
261 31
        $matches = array();
262 31
        if (!preg_match('/@var ([\w\\\\]+)/', $comment, $matches)) {
263 5
            return null;
264
        }
265
266 27
        $type = $matches[1];
267
268 27
        if (self::isPrimitive($type)) {
269 27
            return $type;
270
        }
271
272 20
        $type = self::fullQualifiedName($namespace, $type);
273
274 20
        if (!class_exists($type)) {
275 1
            throw new OrmException("Annotated type {type} could not be found nor loaded", array(
276 1
                'type' => $matches[1]
277 1
            ));
278
        }
279
280 19
        return $type;
281
    }
282
283
    /**
284
     * Get the annotated column name
285
     *
286
     * @param string $class The name of class to retrieve te annotated column name
287
     * @param string $property The property which is annotated by column name
288
     *
289
     * @return string The column name
290
     */
291 1
    private static function getAnnotatedColumnFromProperty($class, $property)
292
    {
293 1
        $rfProperty = new \ReflectionProperty($class, $property);
294 1
        return self::getAnnotatedColumn($rfProperty->getDocComment());
295
    }
296
297
    /**
298
     * Get the annotated column name from document comment string
299
     *
300
     * @param string $comment The document comment which may contain the @column annotation
301
     *
302
     * @return string|null The parsed column name
303
     */
304 30
    private static function getAnnotatedColumn($comment)
305
    {
306 30
        $columnName = null;
307
308 30
        $matches = array();
309 30
        if (preg_match("/@column (\w+)/", $comment, $matches)) {
310 21
            $columnName = $matches[1];
311 21
        }
312 30
        return $columnName;
313
    }
314
315
    /**
316
     * Check whether property is annotated using @id
317
     *
318
     * @param string $comment The document comment which may contain the @id annotation
319
     *
320
     * @return boolean true in case of it is annotated, false otherwise
321
     */
322 22
    private static function isIdAnnotated($comment)
323
    {
324 22
        return preg_match('/@id/', $comment) > 0;
325
    }
326
327
    /**
328
     * Check whether property is annotated using @cascade
329
     *
330
     * @param string $comment The document comment which may contain the @cascade annotation
331
     *
332
     * @return boolean true in case of it is annotated, false otherwise
333
     */
334 13
    private static function isCascadeAnnotated($comment)
335
    {
336 13
        return preg_match('/@cascade/', $comment) > 0;
337
    }
338
339
    /**
340
     * Get the mappedBy parameters from documentation comment
341
     *
342
     * @param string  $comment The documentation comment to parse
343
     *
344
     * @return string The parsed parameters or null
345
     */
346 22
    private static function getAnnotatedMappedByParameters($comment)
347
    {
348 22
        $parameters = null;
349
350 22
        $matches = array();
351 22
        if (preg_match('/@mappedBy\(([^\)].+)\)/', $comment, $matches)) {
352 5
            $parameters = $matches[1];
353 5
        }
354 22
        return $parameters;
355
    }
356
357
    /**
358
     * Checks whether an entity has eager fetch type
359
     *
360
     * @param string $class Name of class of entity
361
     *
362
     * @return boolean true if fetch type is eager, false otherwise
363
     */
364 26
    private static function isEager($class)
365
    {
366 26
        $rf = new \ReflectionClass($class);
367 26
        return preg_match('/@eager/', $rf->getDocComment()) > 0;
368
    }
369
}
370