Completed
Push — master ( ada7af...87ae84 )
by Lee
07:59
created

AnnotationDriver   B

Complexity

Total Complexity 40

Size/Duplication

Total Lines 232
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 94.44%

Importance

Changes 25
Bugs 6 Features 4
Metric Value
wmc 40
c 25
b 6
f 4
lcom 1
cbo 8
dl 0
loc 232
ccs 102
cts 108
cp 0.9444
rs 8.2608

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
D getAllClassNames() 0 43 10
A addExtension() 0 9 3
A removeExtensions() 0 11 3
A isDrestResource() 0 12 3
B loadMetadataForClass() 0 31 6
C processMethods() 0 30 11
A create() 0 6 1
A register() 0 3 1
A registerAnnotations() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like AnnotationDriver 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 AnnotationDriver, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Drest\Mapping\Driver;
4
5
use Doctrine\Common\Annotations;
6
use Drest\DrestException;
7
use Drest\Mapping\Annotation;
8
use Drest\Mapping;
9
10
/**
11
 * The AnnotationDriver reads the mapping metadata from doc block annotations.
12
 * Doesn't require paths / file extensions as entities are pull from the doctrine entity manager
13
 */
14
class AnnotationDriver extends AbstractDriver
15
{
16
17
    /**
18
     * Annotations reader
19
     * @var \Doctrine\Common\Annotations\AnnotationReader $reader
20
     */
21
    private $reader;
22
23
    /**
24
     * Extensions of the files to read
25
     * @var array $paths
26
     */
27
    protected $extensions = [];
28
29
    /**
30
     * The array of class names.
31
     * @var array
32
     */
33
    protected $classNames = [];
34
35
36 47
    public function __construct(Annotations\AnnotationReader $reader, $paths = [])
37
    {
38 47
        parent::__construct($paths);
39
40 47
        $this->addExtension('php');
41
42 47
        $this->reader = $reader;
43 47
    }
44
45
    /**
46
     * Get all the metadata class names known to this driver.
47
     * @throws DrestException
48
     * @return array          $classes
49
     */
50 44
    public function getAllClassNames()
51
    {
52 33
        if (empty($this->classNames)) {
53 33
            if (empty($this->paths)) {
54 1
                throw DrestException::pathToConfigFilesRequired();
55
            }
56 32
            $classes = [];
57 32
            $included = [];
58 32
            foreach ($this->paths as $path) {
59 32
                $iterator = new \RecursiveIteratorIterator(
60 32
                    new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS),
61
                    \RecursiveIteratorIterator::LEAVES_ONLY
62 32
                );
63
64 32
                foreach ($iterator as $file) {
65
                    /* @var \SplFileInfo $file */
66 32
                    if (!in_array($file->getExtension(), $this->extensions)) {
67 2
                        continue;
68
                    }
69
70 44
                    $path = $file->getRealPath();
71 44
                    if (!empty($path)) {
72 32
                        require_once $path;
73 32
                    }
74
75
                    // Register the files we've included here
76 32
                    $included[] = $path;
77 43
                }
78 32
            }
79
80 43
            foreach (get_declared_classes() as $className) {
81 43
                $reflClass = new \ReflectionClass($className);
82 32
                $sourceFile = $reflClass->getFileName();
83 32
                if (in_array($sourceFile, $included) && $this->isDrestResource($className)) {
84 42
                    $classes[] = $className;
85 35
                }
86 32
            }
87
88 32
            $this->classNames = $classes;
89 42
        }
90
91 42
        return $this->classNames;
92 34
    }
93
94
    /**
95
     * Add an extension to look for classes
96
     * @param string $extension - can be a string or an array of extensions
97
     */
98 47
    public function addExtension($extension)
99
    {
100 47
        $extension = (array) $extension;
101 47
        foreach ($extension as $ext) {
102 47
            if (!in_array($ext, $this->extensions)) {
103 47
                $this->extensions[] = strtolower(preg_replace("/[^a-zA-Z0-9.\s]/", "", $ext));
104 47
            }
105 47
        }
106 47
    }
107
108
    /**
109
     * Remove all registered extensions, if an extension name is passed, only remove that entry
110
     * @param string $extension
111
     */
112 1
    public function removeExtensions($extension = null)
113
    {
114 1
        if (is_null($extension)) {
115 1
            $this->extensions = [];
116 1
        } else {
117
            $offset = array_search($extension, $this->extensions);
118
            if ($offset !== false) {
119
                unset($this->extensions[$offset]);
120
            }
121
        }
122 1
    }
123
124
    /**
125
     * Does the class contain a drest resource object
126
     * @param  string $className
127
     * @return bool
128
     */
129 32
    public function isDrestResource($className)
130
    {
131 32
        $classAnnotations = $this->reader->getClassAnnotations(new \ReflectionClass($className));
132
133 32
        foreach ($classAnnotations as $classAnnotation) {
134 32
            if ($classAnnotation instanceof Annotation\Resource) {
135 32
                return true;
136
            }
137 32
        }
138
139 32
        return false;
140
    }
141
142
    /**
143
     * Load metadata for a class name
144
     * @param  object|string         $className - Pass in either the class name, or an instance of that class
145
     * @return Mapping\ClassMetaData|null $metaData - return null if metadata couldn't be populated from annotations
146
     * @throws DrestException
147
     */
148 45
    public function loadMetadataForClass($className)
149
    {
150 45
        $resourceFound = false;
151
152 45
        $class = new \ReflectionClass($className);
153
154 45
        $metadata = new Mapping\ClassMetaData($class);
155 45
        foreach ($this->reader->getClassAnnotations($class) as $annotatedObject) {
156 44
            if ($annotatedObject instanceof Annotation\Resource) {
157 44
                $resourceFound = true;
158
159 44
                if ($annotatedObject->routes === null) {
160 1
                    throw DrestException::annotatedResourceRequiresAtLeastOneServiceDefinition($class->name);
161
                }
162
163 43
                if (is_array($annotatedObject->representations))
164 43
                {
165 36
                    $metadata->addRepresentations($annotatedObject->representations);
166 36
                }
167
168 43
                $this->processRoutes($annotatedObject->routes, $metadata);
169
170 40
                $this->processMethods($class->getMethods(), $metadata);
171
172 38
                $this->checkHandleCalls($metadata->getRoutesMetaData());
173
174 38
            }
175 39
        }
176
177 39
        return ($resourceFound) ? $metadata : null;
0 ignored issues
show
Bug Compatibility introduced by
The expression $resourceFound ? $metadata : null; of type Drest\Mapping\ClassMetaData|null adds the type Drest\Mapping\ClassMetaData to the return on line 177 which is incompatible with the return type declared by the abstract method Drest\Mapping\Driver\Abs...r::loadMetadataForClass of type Drest\Mapping\Driver\ClassMetadata.
Loading history...
178
    }
179
180
181
    /**
182
     * Process the method
183
     * @param \ReflectionMethod[] $methods
184
     * @param Mapping\ClassMetaData $metadata
185
     * @throws DrestException
186
     */
187 40
    protected function processMethods($methods, Mapping\ClassMetaData $metadata)
188
    {
189
        // Set the handle calls
190 40
        foreach ($methods as $method) {
191
            /* @var \ReflectionMethod $method */
192 38
            if ($method->isPublic()) {
193 38
                foreach ($this->reader->getMethodAnnotations($method) as $methodAnnotation) {
194 37
                    if ($methodAnnotation instanceof Annotation\Handle) {
195
                        // Make sure the for is not empty
196 37
                        if (empty($methodAnnotation->for) || !is_string($methodAnnotation->for)) {
197
                            throw DrestException::handleForCannotBeEmpty();
198
                        }
199 37
                        if (($routeMetaData = $metadata->getRouteMetaData($methodAnnotation->for)) === false) {
200 1
                            throw DrestException::handleAnnotationDoesntMatchRouteName($methodAnnotation->for);
201
                        }
202 36
                        if ($routeMetaData->hasHandleCall()) {
203
                            // There is already a handle set for this route
204
                            throw DrestException::handleAlreadyDefinedForRoute($routeMetaData);
205
                        }
206
207 36
                        if (in_array(\DrestCommon\Request\Request::METHOD_GET, $routeMetaData->getVerbs(), true) && !$method->isStatic())
208 36
                        {
209 1
                            throw DrestException::handleForGetRouteMustBeStatic();
210
                        }
211 35
                        $routeMetaData->setHandleCall($method->getName());
212 35
                    }
213 36
                }
214 36
            }
215 38
        }
216 38
    }
217
218
    /**
219
     * Factory method for the Annotation Driver
220
     *
221
     * @param  array|string                 $paths
222
     * @return AnnotationDriver
223
     */
224 47
    public static function create($paths = [])
225
    {
226 47
        $reader = new Annotations\AnnotationReader();
227
228 47
        return new self($reader, (array) $paths);
229
    }
230
231
    /**
232
     * Driver registration template method.
233
     */
234 31
    public static function register() {
235 31
        self::registerAnnotations();
236 31
    }
237
238
    /**
239
     * Register out annotation classes with the annotation registry.
240
     */
241 107
    public static function registerAnnotations()
242
    {
243 107
        Annotations\AnnotationRegistry::registerFile(__DIR__ . '/DrestAnnotations.php');
244 107
    }
245
}
246