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

contributte / api-router / 9228273605

24 May 2024 06:17PM UTC coverage: 88.72% (+0.01%) from 88.71%
9228273605

push

github

f3l1x
PHP: require PHP 8.2+

291 of 328 relevant lines covered (88.72%)

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\PsrCachedReader;
9
use Doctrine\Common\Annotations\Reader;
10
use Nette\Application\IPresenter;
11
use Nette\DI\CompilerExtension;
12
use Nette\DI\ContainerBuilder;
13
use Nette\DI\Definitions\Definition;
14
use Nette\DI\Definitions\Statement;
15
use Nette\PhpGenerator\ClassType as GClassType;
16
use Nette\Schema\Expect;
17
use Nette\Schema\Schema;
18
use ReflectionClass;
19
use ReflectionMethod;
20
use stdClass;
21
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
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
                $routesStatements = [];
1✔
52

53
                foreach ($routes as $route) {
1✔
54
                        $routesStatements[] = new Statement(ApiRoute::class . '::fromArray', [$route->toArray()]);
1✔
55
                }
56

57
                $this->definition = $builder->addDefinition($this->prefix('resolver'))
1✔
58
                        ->setType(ApiRoutesResolver::class)
1✔
59
                        ->addSetup('prepandRoutes', [$builder->getDefinition('router'), $routesStatements]);
1✔
60
        }
1✔
61

62
        public function afterCompile(GClassType $class): void
1✔
63
        {
64
                parent::afterCompile($class);
1✔
65

66
                $class->getMethod('initialize')->addBody('$this->getService(?);', [$this->definition->getName()]);
1✔
67
        }
1✔
68

69
        private function setupReaderAnnotations(stdClass $config): void
1✔
70
        {
71
                AnnotationReader::addGlobalIgnoredName('persistent');
1✔
72
                AnnotationReader::addGlobalIgnoredName('inject');
1✔
73

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

79
        /**
80
         * @param array<mixed> $compilerConfig
81
         */
82
        private function setupReader(array $compilerConfig): void
1✔
83
        {
84
                $this->reader = new PsrCachedReader(
1✔
85
                        new AnnotationReader(),
1✔
86
                        new FilesystemAdapter('ApiRouter.Annotations', 0, $compilerConfig['parameters']['tempDir'] . '/cache'),
1✔
87
                        $compilerConfig['parameters']['debugMode']
1✔
88
                );
89
        }
1✔
90

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

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

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

112
        /**
113
         * @param array<ApiRoute> $routes
114
         */
115
        private function findRoutesInPresenter(string $presenter, array &$routes): void
1✔
116
        {
117
                $r = new ReflectionClass($presenter);
1✔
118

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

121
                if (!$route) {
1✔
122
                        return;
×
123
                }
124

125
                /**
126
                 * Add route to priority-half-sorted list
127
                 */
128
                if ($routes !== [] && !$routes[$route->getPriority()]) {
1✔
129
                        $routes[$route->getPriority()] = [];
×
130
                }
131

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

134
                if (!$route->getPresenter()) {
1✔
135
                        $route->setPresenter(preg_replace('/Presenter$/', '', $r->getShortName()));
1✔
136
                }
137

138
                /**
139
                 * Find apropriate methods
140
                 */
141
                foreach ($r->getMethods() as $method_r) {
1✔
142
                        $this->findPresenterMethodRoute($method_r, $routes, $route);
1✔
143
                }
144

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

154
        /**
155
         * @param array<ApiRoute> $routes
156
         */
157
        private function findPresenterMethodRoute(
1✔
158
                ReflectionMethod $method_reflection,
159
                array &$routes,
160
                ApiRoute $route
161
        ): void
162
        {
163
                $actionRoute = $this->reader->getMethodAnnotation($method_reflection, ApiRoute::class);
1✔
164

165
                /**
166
                 * Get action without that ^action string
167
                 */
168
                $action = lcfirst(preg_replace('/^action/', '', $method_reflection->name));
1✔
169

170
                /**
171
                 * Route can be defined also for particular action
172
                 */
173
                if (!$actionRoute) {
1✔
174
                        $route->setAction($action);
1✔
175

176
                        return;
1✔
177
                }
178

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

181
                /**
182
                 * Action route will inherit presenter name, priority, etc from parent route
183
                 */
184
                $actionRoute->setPresenter($actionRoute->getPresenter() ?: $route->getPresenter());
1✔
185
                $actionRoute->setPriority($actionRoute->getPriority() ?: $route->getPriority());
1✔
186
                $actionRoute->setFormat($actionRoute->getFormat() ?: $route->getFormat());
1✔
187
                $actionRoute->setSection($actionRoute->getSection() ?: $route->getSection());
1✔
188
                $actionRoute->setAction($action, $actionRoute->getMethod() ?: null);
1✔
189

190
                $routes[$route->getPriority()][] = $actionRoute;
1✔
191
        }
1✔
192

193
        /**
194
         * @param array<ApiRoute> $routes
195
         * @return array<ApiRoute>
196
         */
197
        private function sortByPriority(array $routes): array
1✔
198
        {
199
                $return = [];
1✔
200

201
                foreach ($routes as $priority_routes) {
1✔
202
                        foreach ($priority_routes as $route) {
1✔
203
                                $return[] = $route;
1✔
204
                        }
205
                }
206

207
                return $return;
1✔
208
        }
209

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