Completed
Push — master ( c6d651...f3cc34 )
by Pavel
02:14
created

ApiRouterExtension::findRoutesInPresenter()   D

Complexity

Conditions 12
Paths 265

Size

Total Lines 75
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 75
rs 4
cc 12
eloc 32
nc 265
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
19
class ApiRouterExtension extends Nette\DI\CompilerExtension
20
{
21
22
	/**
23
	 * @var array
24
	 */
25
	private $defaults = [
26
		'ignoreAnnotation' => []
27
	];
28
29
	/**
30
	 * @var Doctrine\Common\Annotations\Reader
31
	 */
32
	private $reader;
33
34
35
	/**
36
	 * @return void
37
	 */
38
	public function beforeCompile()
39
	{
40
		$config = $this->_getConfig();
41
42
		$builder = $this->getContainerBuilder();
43
		$compiler_config = $this->compiler->getConfig();
44
45
		$this->setupReader($compiler_config, $config);
46
47
		$routes = $this->findRoutes($builder, $compiler_config, $config);
0 ignored issues
show
Unused Code introduced by
The call to ApiRouterExtension::findRoutes() has too many arguments starting with $config.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
48
49
		$builder->addDefinition($this->prefix('resolver'))
50
			->setClass('Ublaboo\ApiRouter\DI\ApiRoutesResolver')
51
			->addSetup('prepandRoutes', [$builder->getDefinition('router'), $routes])
52
			->addTag('run');
53
	}
54
55
56
	/**
57
	 * @param  array $compiler_config
58
	 * @param  array $config
59
	 * @return void
60
	 */
61
	public function setupReader($compiler_config, $config)
62
	{
63
		/**
64
		 * Prepare AnnotationRegistry
65
		 */
66
		AnnotationRegistry::registerFile(__DIR__ . '/../ApiRoute.php');
67
		AnnotationRegistry::registerFile(__DIR__ . '/../ApiRouteSpec.php');
68
69
		AnnotationReader::addGlobalIgnoredName('persistent');
70
		AnnotationReader::addGlobalIgnoredName('inject');
71
72
		foreach ($config['ignoreAnnotation'] as $ignore) {
73
			AnnotationReader::addGlobalIgnoredName($ignore);
74
		}
75
76
		$cache_path = $compiler_config['parameters']['tempDir'] . '/cache/ApiRouter.Annotations';
77
78
		/**
79
		 * Prepare AnnotationReader - use cached values
80
		 */
81
		$this->reader = new CachedReader(
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Doctrine\Common\Ann...ameters']['debugMode']) of type object<Doctrine\Common\Annotations\CachedReader> is incompatible with the declared type object<Ublaboo\ApiRouter...mon\Annotations\Reader> of property $reader.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
82
			new AnnotationReader,
83
			new FilesystemCache($cache_path),
84
			$compiler_config['parameters']['debugMode']
85
		);
86
	}
87
88
89
	/**
90
	 * @param  Nette\DI\ContainerBuilder $builder
91
	 * @param  array $compiler_config
92
	 * @return array
93
	 */
94
	private function findRoutes(Nette\DI\ContainerBuilder $builder, $compiler_config)
0 ignored issues
show
Unused Code introduced by
The parameter $compiler_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...
95
	{
96
		/**
97
		 * Find all presenters and their routes
98
		 */
99
		$presenters = $builder->findByTag('nette.presenter');
100
		$routes = [];
101
102
		foreach ($presenters as $presenter) {
103
			$this->findRoutesInPresenter($presenter, $routes);
0 ignored issues
show
Bug introduced by
It seems like $routes defined by array() on line 100 can also be of type array; however, Ublaboo\ApiRouter\DI\Api...findRoutesInPresenter() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
104
		}
105
106
		/**
107
		 * Return routes sorted by priority
108
		 */
109
		return $this->sortByPriority($routes);
0 ignored issues
show
Bug introduced by
It seems like $routes can also be of type string; however, Ublaboo\ApiRouter\DI\Api...nsion::sortByPriority() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
110
	}
111
112
113
	/**
114
	 * @param  string $presenter
115
	 * @param  string $routes
116
	 * @return void
117
	 */
118
	public function findRoutesInPresenter($presenter, & $routes)
119
	{
120
		$r = ClassType::from($presenter);
121
122
		$route = $this->reader->getClassAnnotation($r, ApiRoute::class);
123
124
		if (!$route) {
125
			return [];
126
		}
127
128
		/**
129
		 * Add route to priority-sorted list
130
		 */
131
		if (empty($routes[$route->getPriority()])) {
132
			$routes[$route->getPriority()] = [];
133
		}
134
135
		$route->setDescription($r->getAnnotation('description'));
136
137
		if (!$route->getPresenter()) {
138
			$route->setPresenter(preg_replace('/Presenter$/', '', $r->getShortName()));
139
		}
140
141
		/**
142
		 * Find apropriate methods
143
		 */
144
		foreach ($r->getMethods() as $method_r) {
145
			$route_action = $this->reader->getMethodAnnotation($method_r, ApiRoute::class);
146
147
			/**
148
			 * Get action without that ^action string
149
			 */
150
			$action = lcfirst(preg_replace('/^action/', '', $method_r->name));
151
152
			/**
153
			 * Route can be defined also for perticular action
154
			 */
155
			if ($route_action) {
156
				if ($method_r instanceof Nette\Reflection\Method) {
157
					$route_action->setDescription($method_r->getAnnotation('description'));
158
				}
159
160
				/**
161
				 * Action route will inherit presenter name nad priority from parent route
162
				 */
163
				if (!$route_action->getPresenter()) {
164
					$route_action->setPresenter($route->getPresenter());
165
				}
166
167
				if (!$route_action->getPriority()) {
168
					$route_action->setPriority($route->getPriority());
169
				}
170
171
				if (!$route_action->getFormat()) {
172
					$route_action->setFormat($route->getFormat());
173
				}
174
175
				if (!$route_action->getSection()) {
176
					$route_action->setSection($route->getSection());
177
				}
178
179
				if ($route_action->getMethod()) {
180
					$route_action->setAction($action, $route_action->getMethod());
181
				} else {
182
					$route_action->setAction($action);
183
				}
184
185
				$routes[$route->getPriority()][] = $route_action;
186
			} else {
187
				$route->setAction($action);
188
			}
189
		}
190
191
		$routes[$route->getPriority()][] = $route;
192
	}
193
194
195
	/**
196
	 * @param  array  $routes
197
	 * @return array
198
	 */
199
	private function sortByPriority(array $routes)
200
	{
201
		$return = [];
202
203
		foreach ($routes as $priority => $priority_routes) {
204
			foreach ($priority_routes as $route) {
205
				$return[] = $route;
206
			}
207
		}
208
209
		return $return;
210
	}
211
212
213
	/**
214
	 * @return array
215
	 */
216
	private function _getConfig()
217
	{
218
		$config = $this->validateConfig($this->defaults, $this->config);
219
220
		if (!is_array($config['ignoreAnnotation'])) {
221
			$config['ignoreAnnotation'] = [$config['ignoreAnnotation']];
222
		}
223
224
		return (array) $config;
225
	}
226
227
}
228