Test Failed
Pull Request — master (#32)
by Anatoly
04:53
created

AnnotationDirectoryLoader::findClasses()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
c 0
b 0
f 0
dl 0
loc 10
rs 10
cc 2
nc 2
nop 1
1
<?php declare(strict_types=1);
2
3
/**
4
 * It's free open-source software released under the MIT License.
5
 *
6
 * @author Anatoly Fenric <[email protected]>
7
 * @copyright Copyright (c) 2018, Anatoly Fenric
8
 * @license https://github.com/sunrise-php/http-router/blob/master/LICENSE
9
 * @link https://github.com/sunrise-php/http-router
10
 */
11
12
namespace Sunrise\Http\Router\Loader;
13
14
/**
15
 * Import classes
16
 */
17
use Doctrine\Common\Annotations\SimpleAnnotationReader;
18
use Psr\Container\ContainerInterface;
19
use Sunrise\Http\Router\Annotation\Route as AnnotationRoute;
20
use Sunrise\Http\Router\RouteCollection;
21
use Sunrise\Http\Router\RouteCollectionInterface;
22
use Sunrise\Http\Router\RouteFactory;
23
use Sunrise\Http\Router\RouteInterface;
24
use FilesystemIterator;
25
use RecursiveDirectoryIterator;
26
use RecursiveIteratorIterator;
27
use ReflectionClass;
28
use RegexIterator;
29
30
/**
31
 * Import functions
32
 */
33
use function array_diff;
34
use function get_declared_classes;
35
use function iterator_to_array;
36
use function usort;
37
38
/**
39
 * AnnotationDirectoryLoader
40
 */
41
class AnnotationDirectoryLoader implements LoaderInterface
42
{
43
44
    /**
45
     * @var SimpleAnnotationReader
46
     */
47
    private $annotationReader;
48
49
    /**
50
     * @var RouteFactory
51
     */
52
    private $routeFactory;
53
54
    /**
55
     * @var null|ContainerInterface
56
     */
57
    private $container;
58
59
    /**
60
     * Constructor of the class
61
     */
62
    public function __construct()
63
    {
64
        $this->annotationReader = new SimpleAnnotationReader();
65
        $this->annotationReader->addNamespace('Sunrise\Http\Router\Annotation');
66
67
        $this->routeFactory = new RouteFactory();
68
    }
69
70
    /**
71
     * Gets the loader container
72
     *
73
     * @return null|ContainerInterface
74
     */
75
    public function getContainer() : ?ContainerInterface
76
    {
77
        return $this->container;
78
    }
79
80
    /**
81
     * Sets the given container to the loader
82
     *
83
     * @param ContainerInterface $container
84
     *
85
     * @return void
86
     */
87
    public function setContainer(ContainerInterface $container) : void
88
    {
89
        $this->container = $container;
90
    }
91
92
    /**
93
     * {@inheritDoc}
94
     */
95
    public function load($location) : RouteCollectionInterface
96
    {
97
        $annotations = $this->findAnnotations($location);
98
99
        $routes = [];
100
        foreach ($annotations as $annotation) {
101
            $routes[] = $this->routeFactory->createRoute(
102
                $annotation->name,
103
                $annotation->path,
104
                $annotation->methods,
105
                $this->initClass($annotation->source),
106
                $this->initClasses(...$annotation->middlewares),
107
                $annotation->attributes
108
            );
109
        }
110
111
        return new RouteCollection(...$routes);
112
    }
113
114
    /**
115
     * Finds annotations in the given location
116
     *
117
     * @param string $location
118
     *
119
     * @return AnnotationRoute[]
120
     */
121
    private function findAnnotations(string $location) : array
122
    {
123
        $classes = $this->findClasses($location);
124
125
        $annotations = [];
126
        foreach ($classes as $class) {
127
            $annotation = $this->annotationReader->getClassAnnotation(
128
                new ReflectionClass($class),
129
                AnnotationRoute::class
130
            );
131
132
            if ($annotation) {
133
                AnnotationRoute::assertValidSource($class);
134
135
                $annotation->source = $class;
136
                $annotations[] = $annotation;
137
            }
138
        }
139
140
        usort($annotations, function ($a, $b) {
141
            return $b->priority <=> $a->priority;
142
        });
143
144
        return $annotations;
145
    }
146
147
    /**
148
     * Finds classes in the given location
149
     *
150
     * @param string $location
151
     *
152
     * @return string[]
153
     */
154
    private function findClasses(string $location) : array
155
    {
156
        $files = $this->findFiles($location);
157
        $declared = get_declared_classes();
158
159
        foreach ($files as $file) {
160
            require_once $file;
161
        }
162
163
        return array_diff(get_declared_classes(), $declared);
164
    }
165
166
    /**
167
     * Finds files in the given location
168
     *
169
     * @param string $location
170
     *
171
     * @return string[]
172
     */
173
    private function findFiles(string $location) : array
174
    {
175
        $flags = FilesystemIterator::CURRENT_AS_PATHNAME;
176
177
        $directory = new RecursiveDirectoryIterator($location, $flags);
178
        $iterator = new RecursiveIteratorIterator($directory);
179
        $files = new RegexIterator($iterator, '/\.php$/');
180
181
        return iterator_to_array($files);
182
    }
183
184
    /**
185
     * Initializes the given class
186
     *
187
     * @param string $class
188
     *
189
     * @return object
190
     */
191
    private function initClass(string $class)
192
    {
193
        if ($this->container && $this->container->has($class)) {
194
            return $this->container->get($class);
195
        }
196
197
        return new $class;
198
    }
199
200
    /**
201
     * Initializes the given classes
202
     *
203
     * @param string ...$classes
204
     *
205
     * @return object[]
206
     */
207
    private function initClasses(string ...$classes) : array
208
    {
209
        foreach ($classes as &$class) {
210
            $class = $this->initClass($class);
211
        }
212
213
        return $classes;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $classes returns the type array<integer,string> which is incompatible with the documented return type array<mixed,object>.
Loading history...
214
    }
215
}
216