• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

contributte / api-router / 6903388818

17 Nov 2023 11:27AM UTC coverage: 88.71% (+0.5%) from 88.235%
6903388818

push

github

f3l1x
Versions: open 6.1.x

275 of 310 relevant lines covered (88.71%)

0.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.83
/src/DI/ApiRouterExtension.php
1
<?php declare(strict_types = 1);
2

3
namespace Contributte\ApiRouter\DI;
4

5
use Contributte\ApiRouter\ApiRoute;
6
use Contributte\Utils\Annotations;
7
use Doctrine\Common\Annotations\AnnotationReader;
8
use Doctrine\Common\Annotations\AnnotationRegistry;
9
use Doctrine\Common\Annotations\CachedReader;
10
use Doctrine\Common\Annotations\Reader;
11
use Doctrine\Common\Cache\FilesystemCache;
12
use Nette\Application\IPresenter;
13
use Nette\DI\CompilerExtension;
14
use Nette\DI\ContainerBuilder;
15
use Nette\DI\Definitions\Definition;
16
use Nette\PhpGenerator\ClassType as GClassType;
17
use Nette\Schema\Expect;
18
use Nette\Schema\Schema;
19
use ReflectionClass;
20
use ReflectionMethod;
21
use stdClass;
22

23
/**
24
 * @property-read stdClass $config
25
 */
26
class ApiRouterExtension extends CompilerExtension
27
{
28

29
        private ?Reader $reader = null;
30

31
        private ?Definition $definition = null;
32

33
        public function getConfigSchema(): Schema
34
        {
35
                return Expect::structure([
1✔
36
                        'ignoreAnnotation' => Expect::array([]),
1✔
37
                ]);
38
        }
39

40
        public function beforeCompile(): void
41
        {
42
                $config = $this->getConfig();
1✔
43

44
                $builder = $this->getContainerBuilder();
1✔
45
                $compilerConfig = $this->compiler->getConfig();
1✔
46

47
                $this->setupReaderAnnotations($config);
1✔
48
                $this->setupReader($compilerConfig);
1✔
49

50
                $routes = $this->findRoutes($builder);
1✔
51

52
                $this->definition = $builder->addDefinition($this->prefix('resolver'))
1✔
53
                        ->setType(ApiRoutesResolver::class)
1✔
54
                        ->addSetup('prepandRoutes', [$builder->getDefinition('router'), $routes]);
1✔
55
        }
1✔
56

57
        public function afterCompile(GClassType $class): void
1✔
58
        {
59
                parent::afterCompile($class);
1✔
60

61
                $class->getMethod('initialize')->addBody('$this->getService(?);', [$this->definition->getName()]);
1✔
62
        }
1✔
63

64
        private function setupReaderAnnotations(stdClass $config): void
1✔
65
        {
66
                /**
67
                 * Prepare AnnotationRegistry
68
                 */
69
                AnnotationRegistry::registerFile(__DIR__ . '/../ApiRoute.php');
1✔
70
                AnnotationRegistry::registerFile(__DIR__ . '/../ApiRouteSpec.php');
1✔
71

72
                AnnotationReader::addGlobalIgnoredName('persistent');
1✔
73
                AnnotationReader::addGlobalIgnoredName('inject');
1✔
74

75
                foreach ($config->ignoreAnnotation as $ignore) {
1✔
76
                        AnnotationReader::addGlobalIgnoredName($ignore);
×
77
                }
78
        }
1✔
79

80
        /**
81
         * @param array<mixed> $compilerConfig
82
         */
83
        private function setupReader(array $compilerConfig): void
1✔
84
        {
85
                $cachePath = $compilerConfig['parameters']['tempDir'] . '/cache/ApiRouter.Annotations';
1✔
86

87
                /**
88
                 * Prepare AnnotationReader - use cached values
89
                 */
90
                $this->reader = new CachedReader(
1✔
91
                        new AnnotationReader(),
1✔
92
                        new FilesystemCache($cachePath),
1✔
93
                        $compilerConfig['parameters']['debugMode']
1✔
94
                );
95
        }
1✔
96

97
        /**
98
         * @return array<string, ApiRoute[]>
99
         */
100
        private function findRoutes(ContainerBuilder $builder): array
1✔
101
        {
102
                /**
103
                 * Find all presenters and their routes
104
                 */
105
                $presentersDec = $builder->findByType(IPresenter::class);
1✔
106
                $routes = [];
1✔
107

108
                foreach ($presentersDec as $presenterDec) {
1✔
109
                        $this->findRoutesInPresenter($presenterDec->getType(), $routes);
1✔
110
                }
111

112
                /**
113
                 * Return routes sorted by priority
114
                 */
115
                return $this->sortByPriority($routes);
1✔
116
        }
117

118
        /**
119
         * @param array<ApiRoute> $routes
120
         */
121
        private function findRoutesInPresenter(string $presenter, array &$routes): void
1✔
122
        {
123
                $r = new ReflectionClass($presenter);
1✔
124

125
                $route = $this->reader->getClassAnnotation($r, ApiRoute::class);
1✔
126

127
                if (!$route) {
1✔
128
                        return;
×
129
                }
130

131
                /**
132
                 * Add route to priority-half-sorted list
133
                 */
134
                if ($routes !== [] && !$routes[$route->getPriority()]) {
1✔
135
                        $routes[$route->getPriority()] = [];
×
136
                }
137

138
                $route->setDescription(Annotations::getAnnotation($r, 'description'));
1✔
139

140
                if (!$route->getPresenter()) {
1✔
141
                        $route->setPresenter(preg_replace('/Presenter$/', '', $r->getShortName()));
1✔
142
                }
143

144
                /**
145
                 * Find apropriate methods
146
                 */
147
                foreach ($r->getMethods() as $method_r) {
1✔
148
                        $this->findPresenterMethodRoute($method_r, $routes, $route);
1✔
149
                }
150

151
                /**
152
                 * Add ApiRouter annotated presenter route only if there are some remaining
153
                 * methods without ApiRouter annotated presenter method
154
                 */
155
                if ($route->getMethods() !== []) {
1✔
156
                        $routes[$route->getPriority()][] = $route;
1✔
157
                }
158
        }
1✔
159

160
        /**
161
         * @param array<ApiRoute> $routes
162
         */
163
        private function findPresenterMethodRoute(
1✔
164
                ReflectionMethod $method_reflection,
165
                array &$routes,
166
                ApiRoute $route
167
        ): void
168
        {
169
                $actionRoute = $this->reader->getMethodAnnotation($method_reflection, ApiRoute::class);
1✔
170

171
                /**
172
                 * Get action without that ^action string
173
                 */
174
                $action = lcfirst(preg_replace('/^action/', '', $method_reflection->name));
1✔
175

176
                /**
177
                 * Route can be defined also for particular action
178
                 */
179
                if (!$actionRoute) {
1✔
180
                        $route->setAction($action);
1✔
181

182
                        return;
1✔
183
                }
184

185
                $actionRoute->setDescription(Annotations::getAnnotation($method_reflection, 'description'));
1✔
186

187
                /**
188
                 * Action route will inherit presenter name, priority, etc from parent route
189
                 */
190
                $actionRoute->setPresenter($actionRoute->getPresenter() ?: $route->getPresenter());
1✔
191
                $actionRoute->setPriority($actionRoute->getPriority() ?: $route->getPriority());
1✔
192
                $actionRoute->setFormat($actionRoute->getFormat() ?: $route->getFormat());
1✔
193
                $actionRoute->setSection($actionRoute->getSection() ?: $route->getSection());
1✔
194
                $actionRoute->setAction($action, $actionRoute->getMethod() ?: null);
1✔
195

196
                $routes[$route->getPriority()][] = $actionRoute;
1✔
197
        }
1✔
198

199
        /**
200
         * @param array<ApiRoute> $routes
201
         * @return array<ApiRoute>
202
         */
203
        private function sortByPriority(array $routes): array
1✔
204
        {
205
                $return = [];
1✔
206

207
                foreach ($routes as $priority_routes) {
1✔
208
                        foreach ($priority_routes as $route) {
1✔
209
                                $return[] = $route;
1✔
210
                        }
211
                }
212

213
                return $return;
1✔
214
        }
215

216
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc