Completed
Push — master ( f3cc34...3bc02d )
by Pavel
02:01
created

ApiRouterExtension::findRoutesInPresenter()   B

Complexity

Conditions 5
Paths 9

Size

Total Lines 32
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 32
rs 8.439
cc 5
eloc 13
nc 9
nop 2
1
<?php
2
3
/**
4
 * @copyright   Copyright (c) 2016 ublaboo <[email protected]>
5
 * @author      Pavel Janda <[email protected]>
6
 * @package     Ublaboo
7
 */
8
9
namespace Ublaboo\ApiRouter\DI;
10
11
use Ublaboo\ApiRouter\ApiRoute;
12
use Nette\Reflection\ClassType;
13
use Nette;
14
use Doctrine\Common\Annotations\AnnotationReader;
15
use Doctrine\Common\Annotations\AnnotationRegistry;
16
use Doctrine\Common\Annotations\CachedReader;
17
use Doctrine\Common\Cache\FilesystemCache;
18
use Doctrine\Common\Annotations\Reader;
19
20
class ApiRouterExtension extends Nette\DI\CompilerExtension
21
{
22
23
	/**
24
	 * @var array
25
	 */
26
	private $defaults = [
27
		'ignoreAnnotation' => []
28
	];
29
30
	/**
31
	 * @var Reader
32
	 */
33
	private $reader;
34
35
36
	/**
37
	 * @return void
38
	 */
39
	public function beforeCompile()
40
	{
41
		$config = $this->_getConfig();
42
43
		$builder = $this->getContainerBuilder();
44
		$compiler_config = $this->compiler->getConfig();
45
46
		$this->setupReaderAnnotations($config);
47
		$this->setupReader($compiler_config, $config);
48
49
		$routes = $this->findRoutes($builder);
50
51
		$builder->addDefinition($this->prefix('resolver'))
52
			->setClass('Ublaboo\ApiRouter\DI\ApiRoutesResolver')
53
			->addSetup('prepandRoutes', [$builder->getDefinition('router'), $routes])
54
			->addTag('run');
55
	}
56
57
58
	/**
59
	 * [setupReaderAnnotations description]
60
	 * @param  array $config
61
	 * @return void
62
	 */
63
	private function setupReaderAnnotations($config)
64
	{
65
		/**
66
		 * Prepare AnnotationRegistry
67
		 */
68
		AnnotationRegistry::registerFile(__DIR__ . '/../ApiRoute.php');
69
		AnnotationRegistry::registerFile(__DIR__ . '/../ApiRouteSpec.php');
70
71
		AnnotationReader::addGlobalIgnoredName('persistent');
72
		AnnotationReader::addGlobalIgnoredName('inject');
73
74
		foreach ($config['ignoreAnnotation'] as $ignore) {
75
			AnnotationReader::addGlobalIgnoredName($ignore);
76
		}
77
	}
78
79
80
	/**
81
	 * @param  array $compiler_config
82
	 * @param  array $config
83
	 * @return void
84
	 */
85
	private function setupReader($compiler_config, $config)
0 ignored issues
show
Unused Code introduced by
The parameter $config is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
86
	{
87
		$cache_path = $compiler_config['parameters']['tempDir'] . '/cache/ApiRouter.Annotations';
88
89
		/**
90
		 * Prepare AnnotationReader - use cached values
91
		 */
92
		$this->reader = new CachedReader(
93
			new AnnotationReader,
94
			new FilesystemCache($cache_path),
95
			$compiler_config['parameters']['debugMode']
96
		);
97
	}
98
99
100
	/**
101
	 * @param  Nette\DI\ContainerBuilder $builder
102
	 * @return array
103
	 */
104
	private function findRoutes(Nette\DI\ContainerBuilder $builder)
105
	{
106
		/**
107
		 * Find all presenters and their routes
108
		 */
109
		$presenters = $builder->findByTag('nette.presenter');
110
		$routes = [];
111
112
		foreach ($presenters as $presenter) {
113
			$this->findRoutesInPresenter($presenter, $routes);
114
		}
115
116
		/**
117
		 * Return routes sorted by priority
118
		 */
119
		return $this->sortByPriority($routes);
120
	}
121
122
123
	/**
124
	 * @param  string $presenter
125
	 * @param  array $routes
126
	 * @return void
127
	 */
128
	private function findRoutesInPresenter($presenter, & $routes)
129
	{
130
		$r = ClassType::from($presenter);
131
132
		$route = $this->reader->getClassAnnotation($r, ApiRoute::class);
133
134
		if (!$route) {
135
			return [];
136
		}
137
138
		/**
139
		 * Add route to priority-half-sorted list
140
		 */
141
		if (empty($routes[$route->getPriority()])) {
142
			$routes[$route->getPriority()] = [];
143
		}
144
145
		$route->setDescription($r->getAnnotation('description'));
146
147
		if (!$route->getPresenter()) {
148
			$route->setPresenter(preg_replace('/Presenter$/', '', $r->getShortName()));
149
		}
150
151
		/**
152
		 * Find apropriate methods
153
		 */
154
		foreach ($r->getMethods() as $method_r) {
155
			$this->findPresenterMethodRoute($method_r, $routes, $route);
156
		}
157
158
		$routes[$route->getPriority()][] = $route;
159
	}
160
161
162
	/**
163
	 * @param  \ReflectionMethod $method_reflection
164
	 * @param  array             $routes
165
	 * @param  ApiRoute          $route
166
	 * @return void
167
	 */
168
	private function findPresenterMethodRoute(\ReflectionMethod $method_reflection, & $routes, ApiRoute $route)
169
	{
170
		$route_action = $this->reader->getMethodAnnotation($method_reflection, ApiRoute::class);
171
172
		/**
173
		 * Get action without that ^action string
174
		 */
175
		$action = lcfirst(preg_replace('/^action/', '', $method_reflection->name));
176
177
		/**
178
		 * Route can be defined also for perticular action
179
		 */
180
		if ($route_action) {
181
			if ($method_reflection instanceof Nette\Reflection\Method) {
182
				$route_action->setDescription($method_reflection->getAnnotation('description'));
183
			}
184
185
			/**
186
			 * Action route will inherit presenter name, priority, etc from parent route
187
			 */
188
			if (!$route_action->getPresenter()) {
189
				$route_action->setPresenter($route->getPresenter());
190
			}
191
192
			if (!$route_action->getPriority()) {
193
				$route_action->setPriority($route->getPriority());
194
			}
195
196
			if (!$route_action->getFormat()) {
197
				$route_action->setFormat($route->getFormat());
198
			}
199
200
			if (!$route_action->getSection()) {
201
				$route_action->setSection($route->getSection());
202
			}
203
204
			if ($route_action->getMethod()) {
205
				$route_action->setAction($action, $route_action->getMethod());
206
			} else {
207
				$route_action->setAction($action);
208
			}
209
210
			$routes[$route->getPriority()][] = $route_action;
211
		} else {
212
			$route->setAction($action);
213
		}
214
	}
215
216
217
	/**
218
	 * @param  array $routes
219
	 * @return array
220
	 */
221
	private function sortByPriority(array $routes)
222
	{
223
		$return = [];
224
225
		foreach ($routes as $priority => $priority_routes) {
226
			foreach ($priority_routes as $route) {
227
				$return[] = $route;
228
			}
229
		}
230
231
		return $return;
232
	}
233
234
235
	/**
236
	 * @return array
237
	 */
238
	private function _getConfig()
239
	{
240
		$config = $this->validateConfig($this->defaults, $this->config);
241
242
		if (!is_array($config['ignoreAnnotation'])) {
243
			$config['ignoreAnnotation'] = [$config['ignoreAnnotation']];
244
		}
245
246
		return (array) $config;
247
	}
248
249
}
250