Passed
Push — master ( f8275a...0d3330 )
by Roeland
12:33 queued 11s
created

RouteConfig::processSimpleRoutes()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 36
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 21
nc 6
nop 1
dl 0
loc 36
rs 9.2728
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
/**
4
 * @copyright Copyright (c) 2016, ownCloud, Inc.
5
 *
6
 * @author Bernhard Posselt <[email protected]>
7
 * @author Morris Jobke <[email protected]>
8
 * @author Patrick Paysant <[email protected]>
9
 * @author Robin Appelman <[email protected]>
10
 * @author Robin McCorkell <[email protected]>
11
 * @author Roeland Jago Douma <[email protected]>
12
 * @author Thomas Müller <[email protected]>
13
 *
14
 * @license AGPL-3.0
15
 *
16
 * This code is free software: you can redistribute it and/or modify
17
 * it under the terms of the GNU Affero General Public License, version 3,
18
 * as published by the Free Software Foundation.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
 * GNU Affero General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU Affero General Public License, version 3,
26
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
27
 *
28
 */
29
30
namespace OC\AppFramework\Routing;
31
32
use OC\AppFramework\DependencyInjection\DIContainer;
33
use OCP\Route\IRouter;
34
35
/**
36
 * Class RouteConfig
37
 * @package OC\AppFramework\routing
38
 */
39
class RouteConfig {
40
	/** @var DIContainer */
41
	private $container;
42
43
	/** @var IRouter */
44
	private $router;
45
46
	/** @var array */
47
	private $routes;
48
49
	/** @var string */
50
	private $appName;
51
52
	/** @var string[] */
53
	private $controllerNameCache = [];
54
55
	/**
56
	 * @param \OC\AppFramework\DependencyInjection\DIContainer $container
57
	 * @param \OCP\Route\IRouter $router
58
	 * @param array $routes
59
	 * @internal param $appName
60
	 */
61
	public function __construct(DIContainer $container, IRouter $router, $routes) {
62
		$this->routes = $routes;
63
		$this->container = $container;
64
		$this->router = $router;
65
		$this->appName = $container['AppName'];
66
	}
67
68
	/**
69
	 * The routes and resource will be registered to the \OCP\Route\IRouter
70
	 */
71
	public function register() {
72
73
		// parse simple
74
		$this->processSimpleRoutes($this->routes);
75
76
		// parse resources
77
		$this->processResources($this->routes);
78
79
		/*
80
		 * OCS routes go into a different collection
81
		 */
82
		$oldCollection = $this->router->getCurrentCollection();
0 ignored issues
show
Deprecated Code introduced by
The function OCP\Route\IRouter::getCurrentCollection() has been deprecated: 9.0.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

82
		$oldCollection = /** @scrutinizer ignore-deprecated */ $this->router->getCurrentCollection();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
83
		$this->router->useCollection($oldCollection . '.ocs');
0 ignored issues
show
Deprecated Code introduced by
The function OCP\Route\IRouter::useCollection() has been deprecated: 9.0.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

83
		/** @scrutinizer ignore-deprecated */ $this->router->useCollection($oldCollection . '.ocs');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
84
85
		// parse ocs simple routes
86
		$this->processOCS($this->routes);
87
88
		// parse ocs simple routes
89
		$this->processOCSResources($this->routes);
90
91
		$this->router->useCollection($oldCollection);
0 ignored issues
show
Deprecated Code introduced by
The function OCP\Route\IRouter::useCollection() has been deprecated: 9.0.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

91
		/** @scrutinizer ignore-deprecated */ $this->router->useCollection($oldCollection);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
92
	}
93
94
	private function processOCS(array $routes): void {
95
		$ocsRoutes = $routes['ocs'] ?? [];
96
		foreach ($ocsRoutes as $ocsRoute) {
97
			$name = $ocsRoute['name'];
98
			$postfix = $ocsRoute['postfix'] ?? '';
99
			$root = $ocsRoute['root'] ?? '/apps/' . $this->appName;
100
101
			$url = $root . $ocsRoute['url'];
102
			$verb = strtoupper($ocsRoute['verb'] ?? 'GET');
103
104
			$split = explode('#', $name, 2);
105
			if (count($split) !== 2) {
106
				throw new \UnexpectedValueException('Invalid route name');
107
			}
108
			list($controller, $action) = $split;
109
110
			$controllerName = $this->buildControllerName($controller);
111
			$actionName = $this->buildActionName($action);
112
113
			$routeName = 'ocs.' . $this->appName . '.' . $controller . '.' . $action . $postfix;
114
115
			// register the route
116
			$handler = new RouteActionHandler($this->container, $controllerName, $actionName);
117
118
			$router = $this->router->create($routeName, $url)
0 ignored issues
show
Deprecated Code introduced by
The function OCP\Route\IRouter::create() has been deprecated: 9.0.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

118
			$router = /** @scrutinizer ignore-deprecated */ $this->router->create($routeName, $url)

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
119
				->method($verb)
120
				->action($handler);
121
122
			// optionally register requirements for route. This is used to
123
			// tell the route parser how url parameters should be matched
124
			if(array_key_exists('requirements', $ocsRoute)) {
125
				$router->requirements($ocsRoute['requirements']);
126
			}
127
128
			// optionally register defaults for route. This is used to
129
			// tell the route parser how url parameters should be default valued
130
			if(array_key_exists('defaults', $ocsRoute)) {
131
				$router->defaults($ocsRoute['defaults']);
132
			}
133
		}
134
	}
135
136
	/**
137
	 * Creates one route base on the give configuration
138
	 * @param array $routes
139
	 * @throws \UnexpectedValueException
140
	 */
141
	private function processSimpleRoutes(array $routes): void {
142
		$simpleRoutes = $routes['routes'] ?? [];
143
		foreach ($simpleRoutes as $simpleRoute) {
144
			$name = $simpleRoute['name'];
145
			$postfix = $simpleRoute['postfix'] ?? '';
146
147
			$url = $simpleRoute['url'];
148
			$verb = strtoupper($simpleRoute['verb'] ?? 'GET');
149
150
			$split = explode('#', $name, 2);
151
			if (count($split) !== 2) {
152
				throw new \UnexpectedValueException('Invalid route name');
153
			}
154
			list($controller, $action) = $split;
155
156
			$controllerName = $this->buildControllerName($controller);
157
			$actionName = $this->buildActionName($action);
158
159
			$routeName = $this->appName . '.' . $controller . '.' . $action . $postfix;
160
161
			// register the route
162
			$handler = new RouteActionHandler($this->container, $controllerName, $actionName);
163
			$router = $this->router->create($routeName, $url)
0 ignored issues
show
Deprecated Code introduced by
The function OCP\Route\IRouter::create() has been deprecated: 9.0.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

163
			$router = /** @scrutinizer ignore-deprecated */ $this->router->create($routeName, $url)

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
164
							->method($verb)
165
							->action($handler);
166
167
			// optionally register requirements for route. This is used to
168
			// tell the route parser how url parameters should be matched
169
			if(array_key_exists('requirements', $simpleRoute)) {
170
				$router->requirements($simpleRoute['requirements']);
171
			}
172
173
			// optionally register defaults for route. This is used to
174
			// tell the route parser how url parameters should be default valued
175
			if(array_key_exists('defaults', $simpleRoute)) {
176
				$router->defaults($simpleRoute['defaults']);
177
			}
178
		}
179
	}
180
181
	/**
182
	 * For a given name and url restful OCS routes are created:
183
	 *  - index
184
	 *  - show
185
	 *  - create
186
	 *  - update
187
	 *  - destroy
188
	 *
189
	 * @param array $routes
190
	 */
191
	private function processOCSResources(array $routes): void {
192
		// declaration of all restful actions
193
		$actions = [
194
			['name' => 'index', 'verb' => 'GET', 'on-collection' => true],
195
			['name' => 'show', 'verb' => 'GET'],
196
			['name' => 'create', 'verb' => 'POST', 'on-collection' => true],
197
			['name' => 'update', 'verb' => 'PUT'],
198
			['name' => 'destroy', 'verb' => 'DELETE'],
199
		];
200
201
		$resources = $routes['ocs-resources'] ?? [];
202
		foreach ($resources as $resource => $config) {
203
			$root = $config['root'] ?? '/apps/' . $this->appName;
204
205
			// the url parameter used as id to the resource
206
			foreach($actions as $action) {
207
				$url = $root . $config['url'];
208
				$method = $action['name'];
209
				$verb = strtoupper($action['verb'] ?? 'GET');
210
				$collectionAction = $action['on-collection'] ?? false;
211
				if (!$collectionAction) {
212
					$url .= '/{id}';
213
				}
214
				if (isset($action['url-postfix'])) {
215
					$url .= '/' . $action['url-postfix'];
216
				}
217
218
				$controller = $resource;
219
220
				$controllerName = $this->buildControllerName($controller);
221
				$actionName = $this->buildActionName($method);
222
223
				$routeName = 'ocs.' . $this->appName . '.' . strtolower($resource) . '.' . strtolower($method);
224
225
				$this->router->create($routeName, $url)->method($verb)->action(
0 ignored issues
show
Deprecated Code introduced by
The function OCP\Route\IRouter::create() has been deprecated: 9.0.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

225
				/** @scrutinizer ignore-deprecated */ $this->router->create($routeName, $url)->method($verb)->action(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
226
					new RouteActionHandler($this->container, $controllerName, $actionName)
227
				);
228
			}
229
		}
230
	}
231
232
	/**
233
	 * For a given name and url restful routes are created:
234
	 *  - index
235
	 *  - show
236
	 *  - create
237
	 *  - update
238
	 *  - destroy
239
	 *
240
	 * @param array $routes
241
	 */
242
	private function processResources(array $routes): void {
243
		// declaration of all restful actions
244
		$actions = [
245
			['name' => 'index', 'verb' => 'GET', 'on-collection' => true],
246
			['name' => 'show', 'verb' => 'GET'],
247
			['name' => 'create', 'verb' => 'POST', 'on-collection' => true],
248
			['name' => 'update', 'verb' => 'PUT'],
249
			['name' => 'destroy', 'verb' => 'DELETE'],
250
		];
251
252
		$resources = $routes['resources'] ?? [];
253
		foreach ($resources as $resource => $config) {
254
255
			// the url parameter used as id to the resource
256
			foreach($actions as $action) {
257
				$url = $config['url'];
258
				$method = $action['name'];
259
				$verb = strtoupper($action['verb'] ?? 'GET');
260
				$collectionAction = $action['on-collection'] ?? false;
261
				if (!$collectionAction) {
262
					$url .= '/{id}';
263
				}
264
				if (isset($action['url-postfix'])) {
265
					$url .= '/' . $action['url-postfix'];
266
				}
267
268
				$controller = $resource;
269
270
				$controllerName = $this->buildControllerName($controller);
271
				$actionName = $this->buildActionName($method);
272
273
				$routeName = $this->appName . '.' . strtolower($resource) . '.' . strtolower($method);
274
275
				$this->router->create($routeName, $url)->method($verb)->action(
0 ignored issues
show
Deprecated Code introduced by
The function OCP\Route\IRouter::create() has been deprecated: 9.0.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

275
				/** @scrutinizer ignore-deprecated */ $this->router->create($routeName, $url)->method($verb)->action(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
276
					new RouteActionHandler($this->container, $controllerName, $actionName)
277
				);
278
			}
279
		}
280
	}
281
282
	/**
283
	 * Based on a given route name the controller name is generated
284
	 * @param string $controller
285
	 * @return string
286
	 */
287
	private function buildControllerName(string $controller): string {
288
		if (!isset($this->controllerNameCache[$controller])) {
289
			$this->controllerNameCache[$controller] = $this->underScoreToCamelCase(ucfirst($controller)) . 'Controller';
290
		}
291
		return $this->controllerNameCache[$controller];
292
	}
293
294
	/**
295
	 * Based on the action part of the route name the controller method name is generated
296
	 * @param string $action
297
	 * @return string
298
	 */
299
	private function buildActionName(string $action): string {
300
		return $this->underScoreToCamelCase($action);
301
	}
302
303
	/**
304
	 * Underscored strings are converted to camel case strings
305
	 * @param string $str
306
	 * @return string
307
	 */
308
	private function underScoreToCamelCase(string $str): string {
309
		$pattern = '/_[a-z]?/';
310
		return preg_replace_callback(
311
			$pattern,
312
			function ($matches) {
313
				return strtoupper(ltrim($matches[0], '_'));
314
			},
315
			$str);
316
	}
317
}
318