AnnotationDriver::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 5
cts 5
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 2
crap 1
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 43
            }
56 44
            $classes = [];
57 32
            $included = [];
58 32
            foreach ($this->paths as $path) {
59 43
                $iterator = new \RecursiveIteratorIterator(
60 33
                    new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS),
61
                    \RecursiveIteratorIterator::LEAVES_ONLY
62 43
                );
63
64 32
                foreach ($iterator as $file) {
65
                    /* @var \SplFileInfo $file */
66 43
                    if (!in_array($file->getExtension(), $this->extensions)) {
67 1
                        continue;
68
                    }
69
70 35
                    $path = $file->getRealPath();
71 32
                    if (!empty($path)) {
72 32
                        require_once $path;
73 32
                    }
74
75
                    // Register the files we've included here
76 42
                    $included[] = $path;
77 35
                }
78 32
            }
79
80 32
            foreach (get_declared_classes() as $className) {
81 42
                $reflClass = new \ReflectionClass($className);
82 35
                $sourceFile = $reflClass->getFileName();
83 32
                if (in_array($sourceFile, $included) && $this->isDrestResource($className)) {
84 32
                    $classes[] = $className;
85 32
                }
86 42
            }
87
88 32
            $this->classNames = $classes;
89 32
        }
90
91 42
        return $this->classNames;
92
    }
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 38
            }
172 39
        }
173
174 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 174 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...
175
    }
176
177
178
    /**
179
     * Process the method
180
     * @param \ReflectionMethod[] $methods
181
     * @param Mapping\ClassMetaData $metadata
182
     * @throws DrestException
183
     */
184 40
    protected function processMethods($methods, Mapping\ClassMetaData $metadata)
185
    {
186
        // Set the handle calls
187 40
        foreach ($methods as $method) {
188
            /* @var \ReflectionMethod $method */
189 38
            if ($method->isPublic()) {
190 38
                foreach ($this->reader->getMethodAnnotations($method) as $methodAnnotation) {
191 37
                    if ($methodAnnotation instanceof Annotation\Handle) {
192
                        // Make sure the for is not empty
193 37
                        if (empty($methodAnnotation->for) || !is_string($methodAnnotation->for)) {
194
                            throw DrestException::handleForCannotBeEmpty();
195
                        }
196 37
                        if (($routeMetaData = $metadata->getRouteMetaData($methodAnnotation->for)) === false) {
197 1
                            throw DrestException::handleAnnotationDoesntMatchRouteName($methodAnnotation->for);
198
                        }
199 36
                        if ($routeMetaData->hasHandleCall()) {
200
                            // There is already a handle set for this route
201
                            throw DrestException::handleAlreadyDefinedForRoute($routeMetaData);
202
                        }
203
204 36
                        if (in_array(\DrestCommon\Request\Request::METHOD_GET, $routeMetaData->getVerbs(), true) && !$method->isStatic())
205 36
                        {
206 1
                            throw DrestException::handleForGetRouteMustBeStatic();
207
                        }
208 35
                        $routeMetaData->setHandleCall($method->getName());
209 35
                    }
210 36
                }
211 36
            }
212 38
        }
213 38
    }
214
215
    /**
216
     * Factory method for the Annotation Driver
217
     *
218
     * @param  array|string                 $paths
219
     * @return AnnotationDriver
220
     */
221 47
    public static function create($paths = [])
222
    {
223 47
        $reader = new Annotations\AnnotationReader();
224
225 47
        return new self($reader, (array) $paths);
226
    }
227
228
    /**
229
     * Driver registration template method.
230
     */
231 31
    public static function register() {
232 31
        self::registerAnnotations();
233 31
    }
234
235
    /**
236
     * Register out annotation classes with the annotation registry.
237
     */
238 107
    public static function registerAnnotations()
239
    {
240 107
        Annotations\AnnotationRegistry::registerFile(__DIR__ . '/DrestAnnotations.php');
241 107
    }
242
}
243