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

contributte / api-router / 3782097069

pending completion
3782097069

push

github

Milan Felix Ć ulc
Readme: mention extension + examples

270 of 306 relevant lines covered (88.24%)

0.88 hits per line

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

94.59
/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 ReflectionClass;
18
use ReflectionMethod;
19

20
class ApiRouterExtension extends CompilerExtension
21
{
22

23
        /** @var array */
24
        private $defaults = [
25
                'ignoreAnnotation' => [],
26
        ];
27

28
        /** @var Reader */
29
        private $reader;
30

31
        /** @var Definition */
32
        private $definition;
33

34
        public function beforeCompile(): void
35
        {
36
                $config = $this->_getConfig();
1✔
37

38
                $builder = $this->getContainerBuilder();
1✔
39
                $compiler_config = $this->compiler->getConfig();
1✔
40

41
                $this->setupReaderAnnotations($config);
1✔
42
                $this->setupReader($compiler_config);
1✔
43

44
                $routes = $this->findRoutes($builder);
1✔
45

46
                $this->definition = $builder->addDefinition($this->prefix('resolver'))
1✔
47
                        ->setClass(ApiRoutesResolver::class)
1✔
48
                        ->addSetup('prepandRoutes', [$builder->getDefinition('router'), $routes]);
1✔
49
        }
1✔
50

51

52
        public function afterCompile(GClassType $class): void
1✔
53
        {
54
                parent::afterCompile($class);
1✔
55

56
                $class->getMethod('initialize')->addBody('$this->getService(?);', [$this->definition->getName()]);
1✔
57
        }
1✔
58

59

60
        private function setupReaderAnnotations(array $config): void
1✔
61
        {
62
                /**
63
                 * Prepare AnnotationRegistry
64
                 */
65
                AnnotationRegistry::registerFile(__DIR__ . '/../ApiRoute.php');
1✔
66
                AnnotationRegistry::registerFile(__DIR__ . '/../ApiRouteSpec.php');
1✔
67

68
                AnnotationReader::addGlobalIgnoredName('persistent');
1✔
69
                AnnotationReader::addGlobalIgnoredName('inject');
1✔
70

71
                foreach ($config['ignoreAnnotation'] as $ignore) {
1✔
72
                        AnnotationReader::addGlobalIgnoredName($ignore);
×
73
                }
74
        }
1✔
75

76

77
        private function setupReader(array $compiler_config): void
1✔
78
        {
79
                $cache_path = $compiler_config['parameters']['tempDir'] . '/cache/ApiRouter.Annotations';
1✔
80

81
                /**
82
                 * Prepare AnnotationReader - use cached values
83
                 */
84
                $this->reader = new CachedReader(
1✔
85
                        new AnnotationReader(),
1✔
86
                        new FilesystemCache($cache_path),
1✔
87
                        $compiler_config['parameters']['debugMode']
1✔
88
                );
89
        }
1✔
90

91

92
        private function findRoutes(ContainerBuilder $builder): array
1✔
93
        {
94
                /**
95
                 * Find all presenters and their routes
96
                 */
97
                $presentersDec = $builder->findByType(IPresenter::class);
1✔
98
                $routes = [];
1✔
99

100
                foreach ($presentersDec as $presenterDec) {
1✔
101
                        $this->findRoutesInPresenter($presenterDec->getType(), $routes);
1✔
102
                }
103

104
                /**
105
                 * Return routes sorted by priority
106
                 */
107
                return $this->sortByPriority($routes);
1✔
108
        }
109

110

111
        private function findRoutesInPresenter(string $presenter, array &$routes): void
1✔
112
        {
113
                $r = new ReflectionClass($presenter);
1✔
114

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

117
                if (!$route) {
1✔
118
                        return;
×
119
                }
120

121
                /**
122
                 * Add route to priority-half-sorted list
123
                 */
124
                if ($routes !== [] && !$routes[$route->getPriority()]) {
1✔
125
                        $routes[$route->getPriority()] = [];
×
126
                }
127

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

130
                if (!$route->getPresenter()) {
1✔
131
                        $route->setPresenter(preg_replace('/Presenter$/', '', $r->getShortName()));
1✔
132
                }
133

134
                /**
135
                 * Find apropriate methods
136
                 */
137
                foreach ($r->getMethods() as $method_r) {
1✔
138
                        $this->findPresenterMethodRoute($method_r, $routes, $route);
1✔
139
                }
140

141
                /**
142
                 * Add ApiRouter annotated presenter route only if there are some remaining
143
                 * methods without ApiRouter annotated presenter method
144
                 */
145
                if ($route->getMethods() !== []) {
1✔
146
                        $routes[$route->getPriority()][] = $route;
1✔
147
                }
148
        }
1✔
149

150

151
        private function findPresenterMethodRoute(
1✔
152
                ReflectionMethod $method_reflection,
153
                array &$routes,
154
                ApiRoute $route
155
        ): void
156
        {
157
                $action_route = $this->reader->getMethodAnnotation($method_reflection, ApiRoute::class);
1✔
158

159
                /**
160
                 * Get action without that ^action string
161
                 */
162
                $action = lcfirst(preg_replace('/^action/', '', $method_reflection->name));
1✔
163

164
                /**
165
                 * Route can be defined also for particular action
166
                 */
167
                if (!$action_route) {
1✔
168
                        $route->setAction($action);
1✔
169

170
                        return;
1✔
171
                }
172

173
                $action_route->setDescription(Annotations::getAnnotation($method_reflection, 'description'));
1✔
174

175
                /**
176
                 * Action route will inherit presenter name, priority, etc from parent route
177
                 */
178
                $action_route->setPresenter($action_route->getPresenter() ?: $route->getPresenter());
1✔
179
                $action_route->setPriority($action_route->getPriority() ?: $route->getPriority());
1✔
180
                $action_route->setFormat($action_route->getFormat() ?: $route->getFormat());
1✔
181
                $action_route->setSection($action_route->getSection() ?: $route->getSection());
1✔
182
                $action_route->setAction($action, $action_route->getMethod() ?: null);
1✔
183

184
                $routes[$route->getPriority()][] = $action_route;
1✔
185
        }
1✔
186

187

188
        private function sortByPriority(array $routes): array
1✔
189
        {
190
                $return = [];
1✔
191

192
                foreach ($routes as $priority => $priority_routes) {
1✔
193
                        foreach ($priority_routes as $route) {
1✔
194
                                $return[] = $route;
1✔
195
                        }
196
                }
197

198
                return $return;
1✔
199
        }
200

201

202
        private function _getConfig(): array
203
        {
204
                $config = $this->validateConfig($this->defaults, $this->config);
1✔
205

206
                if (!is_array($config['ignoreAnnotation'])) {
1✔
207
                        $config['ignoreAnnotation'] = [$config['ignoreAnnotation']];
×
208
                }
209

210
                return (array) $config;
1✔
211
        }
212

213
}
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

© 2026 Coveralls, Inc