Passed
Pull Request — master (#32)
by Anatoly
02:11
created

AnnotationDirectoryLoader::findFiles()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 1
dl 0
loc 7
ccs 5
cts 5
cp 1
crap 1
rs 10
c 0
b 0
f 0
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\Route as BaseRoute;
21
use RecursiveDirectoryIterator;
22
use RecursiveIteratorIterator;
23
use ReflectionClass;
24
use RegexIterator;
25
26
/**
27
 * Import functions
28
 */
29
use function array_diff;
30
use function get_declared_classes;
31
use function iterator_to_array;
32
use function usort;
33
34
/**
35
 * AnnotationDirectoryLoader
36
 */
37
class AnnotationDirectoryLoader implements LoaderInterface
38
{
39
40
    /**
41
     * @var SimpleAnnotationReader
42
     */
43
    private $annotationReader;
44
45
    /**
46
     * @var null|ContainerInterface
47
     */
48
    private $container;
49
50
    /**
51
     * Constructor of the class
52
     */
53 20
    public function __construct()
54
    {
55 20
        $this->annotationReader = new SimpleAnnotationReader();
56 20
        $this->annotationReader->addNamespace('Sunrise\Http\Router\Annotation');
57 20
    }
58
59
    /**
60
     * Gets the loader container
61
     *
62
     * @return null|ContainerInterface
63
     */
64 1
    public function getContainer() : ?ContainerInterface
65
    {
66 1
        return $this->container;
67
    }
68
69
    /**
70
     * Sets the given container to the loader
71
     *
72
     * @param ContainerInterface $container
73
     *
74
     * @return void
75
     */
76 2
    public function setContainer(ContainerInterface $container) : void
77
    {
78 2
        $this->container = $container;
79 2
    }
80
81
    /**
82
     * {@inheritDoc}
83
     */
84 18
    public function load($destination) : array
85
    {
86 18
        $annotations = $this->findAnnotations($destination);
87
88 2
        $routes = [];
89 2
        foreach ($annotations as $annotation) {
90 2
            $routes[] = new BaseRoute(
91 2
                $annotation->name,
92 2
                $annotation->path,
93 2
                $annotation->methods,
94 2
                $this->initClass($annotation->source),
95 2
                $this->initClasses(...$annotation->middlewares),
96 2
                $annotation->attributes
97
            );
98
        }
99
100 2
        return $routes;
101
    }
102
103
    /**
104
     * Finds annotations in the given destination
105
     *
106
     * @param string $destination
107
     *
108
     * @return object[]
109
     */
110 18
    private function findAnnotations(string $destination) : array
111
    {
112 18
        $classes = $this->findClasses($destination);
113
114 18
        $annotations = [];
115 18
        foreach ($classes as $class) {
116 18
            $annotation = $this->annotationReader->getClassAnnotation(
117 18
                new ReflectionClass($class),
118 18
                AnnotationRoute::class
119
            );
120
121 3
            if ($annotation) {
122 2
                $annotation->source = $class;
123 3
                $annotations[] = $annotation;
124
            }
125
        }
126
127
        usort($annotations, function ($a, $b) {
128 1
            return $b->priority <=> $a->priority;
129 2
        });
130
131 2
        return $annotations;
132
    }
133
134
    /**
135
     * Finds classes in the given destination
136
     *
137
     * @param string $destination
138
     *
139
     * @return string[]
140
     */
141 18
    private function findClasses(string $destination) : array
142
    {
143 18
        $files = $this->findFiles($destination);
144 18
        $declared = get_declared_classes();
145
146 18
        foreach ($files as $file) {
147 18
            require_once $file->getRealPath();
148
        }
149
150 18
        return array_diff(get_declared_classes(), $declared);
151
    }
152
153
    /**
154
     * Finds files in the given destination
155
     *
156
     * @param string $destination
157
     *
158
     * @return string[]
159
     */
160 18
    private function findFiles(string $destination) : array
161
    {
162 18
        $directory = new RecursiveDirectoryIterator($destination);
163 18
        $iterator = new RecursiveIteratorIterator($directory);
164 18
        $files = new RegexIterator($iterator, '/\.php$/');
165
166 18
        return iterator_to_array($files);
167
    }
168
169
    /**
170
     * Initializes the given class
171
     *
172
     * @param string $class
173
     *
174
     * @return object
175
     */
176 2
    private function initClass(string $class)
177
    {
178 2
        if ($this->container && $this->container->has($class)) {
179 1
            return $this->container->get($class);
180
        }
181
182 2
        return new $class;
183
    }
184
185
    /**
186
     * Initializes the given classes
187
     *
188
     * @param string ...$classes
189
     *
190
     * @return object[]
191
     */
192 2
    private function initClasses(string ...$classes) : array
193
    {
194 2
        foreach ($classes as &$class) {
195 2
            $class = $this->initClass($class);
196
        }
197
198 2
        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...
199
    }
200
}
201