Passed
Push — master ( aff13a...33c34d )
by Morris
11:22 queued 10s
created

RouteConfig::processOCS()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 38
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 22
nc 6
nop 1
dl 0
loc 38
rs 9.2568
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\AppFramework\App;
34
use OCP\Route\IRouter;
35
36
/**
37
 * Class RouteConfig
38
 * @package OC\AppFramework\routing
39
 */
40
class RouteConfig {
41
	/** @var DIContainer */
42
	private $container;
43
44
	/** @var IRouter */
45
	private $router;
46
47
	/** @var array */
48
	private $routes;
49
50
	/** @var string */
51
	private $appName;
52
53
	/** @var string[] */
54
	private $controllerNameCache = [];
55
56
	/**
57
	 * @param \OC\AppFramework\DependencyInjection\DIContainer $container
58
	 * @param \OCP\Route\IRouter $router
59
	 * @param array $routes
60
	 * @internal param $appName
61
	 */
62
	public function __construct(DIContainer $container, IRouter $router, $routes) {
63
		$this->routes = $routes;
64
		$this->container = $container;
65
		$this->router = $router;
66
		$this->appName = $container['AppName'];
67
	}
68
69
	/**
70
	 * The routes and resource will be registered to the \OCP\Route\IRouter
71
	 */
72
	public function register() {
73
74
		// parse simple
75
		$this->processSimpleRoutes($this->routes);
76
77
		// parse resources
78
		$this->processResources($this->routes);
79
80
		/*
81
		 * OCS routes go into a different collection
82
		 */
83
		$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

83
		$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...
84
		$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

84
		/** @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...
85
86
		// parse ocs simple routes
87
		$this->processOCS($this->routes);
88
89
		// parse ocs simple routes
90
		$this->processOCSResources($this->routes);
91
92
		$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

92
		/** @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...
93
	}
94
95
	private function processOCS(array $routes): void {
96
		$ocsRoutes = $routes['ocs'] ?? [];
97
		foreach ($ocsRoutes as $ocsRoute) {
98
			$name = $ocsRoute['name'];
99
			$postfix = $ocsRoute['postfix'] ?? '';
100
			$root = $ocsRoute['root'] ?? '/apps/' . $this->appName;
101
102
			$url = $root . $ocsRoute['url'];
103
			$verb = strtoupper($ocsRoute['verb'] ?? 'GET');
104
105
			$split = explode('#', $name, 2);
106
			if (count($split) !== 2) {
107
				throw new \UnexpectedValueException('Invalid route name');
108
			}
109
			list($controller, $action) = $split;
110
111
			$controllerName = $this->buildControllerName($controller);
112
			$actionName = $this->buildActionName($action);
113
114
			$routeName = 'ocs.' . $this->appName . '.' . $controller . '.' . $action . $postfix;
115
116
			// register the route
117
			$handler = new RouteActionHandler($this->container, $controllerName, $actionName);
118
119
			$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

119
			$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...
120
				->method($verb)
121
				->action($handler);
122
123
			// optionally register requirements for route. This is used to
124
			// tell the route parser how url parameters should be matched
125
			if(array_key_exists('requirements', $ocsRoute)) {
126
				$router->requirements($ocsRoute['requirements']);
127
			}
128
129
			// optionally register defaults for route. This is used to
130
			// tell the route parser how url parameters should be default valued
131
			if(array_key_exists('defaults', $ocsRoute)) {
132
				$router->defaults($ocsRoute['defaults']);
133
			}
134
		}
135
	}
136
137
	/**
138
	 * Creates one route base on the give configuration
139
	 * @param array $routes
140
	 * @throws \UnexpectedValueException
141
	 */
142
	private function processSimpleRoutes(array $routes): void {
143
		$simpleRoutes = $routes['routes'] ?? [];
144
		foreach ($simpleRoutes as $simpleRoute) {
145
			$name = $simpleRoute['name'];
146
			$postfix = $simpleRoute['postfix'] ?? '';
147
148
			$url = $simpleRoute['url'];
149
			$verb = strtoupper($simpleRoute['verb'] ?? 'GET');
150
151
			$split = explode('#', $name, 2);
152
			if (count($split) !== 2) {
153
				throw new \UnexpectedValueException('Invalid route name');
154
			}
155
			list($controller, $action) = $split;
156
157
			$controllerName = $this->buildControllerName($controller);
158
			$actionName = $this->buildActionName($action);
159
			$appName = $simpleRoute['app'] ?? $this->appName;
160
161
			if (isset($simpleRoute['app'])) {
162
				// Legacy routes that need to be globally available while they are handled by an app
163
				// E.g. '/f/{id}', '/s/{token}', '/call/{token}', …
164
				$controllerName = str_replace('controllerController', 'Controller', $controllerName);
165
				if ($controllerName === 'PublicpreviewController') {
166
					$controllerName = 'PublicPreviewController';
167
				} else if ($controllerName === 'RequesthandlerController') {
168
					$controllerName = 'RequestHandlerController';
169
				}
170
				$controllerName = App::buildAppNamespace($appName) . '\\Controller\\' . $controllerName;
171
			}
172
173
			$routeName = $appName . '.' . $controller . '.' . $action . $postfix;
174
175
			// register the route
176
			$handler = new RouteActionHandler($this->container, $controllerName, $actionName);
177
			$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

177
			$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...
178
							->method($verb)
179
							->action($handler);
180
181
			// optionally register requirements for route. This is used to
182
			// tell the route parser how url parameters should be matched
183
			if(array_key_exists('requirements', $simpleRoute)) {
184
				$router->requirements($simpleRoute['requirements']);
185
			}
186
187
			// optionally register defaults for route. This is used to
188
			// tell the route parser how url parameters should be default valued
189
			if(array_key_exists('defaults', $simpleRoute)) {
190
				$router->defaults($simpleRoute['defaults']);
191
			}
192
		}
193
	}
194
195
	/**
196
	 * For a given name and url restful OCS routes are created:
197
	 *  - index
198
	 *  - show
199
	 *  - create
200
	 *  - update
201
	 *  - destroy
202
	 *
203
	 * @param array $routes
204
	 */
205
	private function processOCSResources(array $routes): void {
206
		// declaration of all restful actions
207
		$actions = [
208
			['name' => 'index', 'verb' => 'GET', 'on-collection' => true],
209
			['name' => 'show', 'verb' => 'GET'],
210
			['name' => 'create', 'verb' => 'POST', 'on-collection' => true],
211
			['name' => 'update', 'verb' => 'PUT'],
212
			['name' => 'destroy', 'verb' => 'DELETE'],
213
		];
214
215
		$resources = $routes['ocs-resources'] ?? [];
216
		foreach ($resources as $resource => $config) {
217
			$root = $config['root'] ?? '/apps/' . $this->appName;
218
219
			// the url parameter used as id to the resource
220
			foreach($actions as $action) {
221
				$url = $root . $config['url'];
222
				$method = $action['name'];
223
				$verb = strtoupper($action['verb'] ?? 'GET');
224
				$collectionAction = $action['on-collection'] ?? false;
225
				if (!$collectionAction) {
226
					$url .= '/{id}';
227
				}
228
				if (isset($action['url-postfix'])) {
229
					$url .= '/' . $action['url-postfix'];
230
				}
231
232
				$controller = $resource;
233
234
				$controllerName = $this->buildControllerName($controller);
235
				$actionName = $this->buildActionName($method);
236
237
				$routeName = 'ocs.' . $this->appName . '.' . strtolower($resource) . '.' . strtolower($method);
238
239
				$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

239
				/** @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...
240
					new RouteActionHandler($this->container, $controllerName, $actionName)
241
				);
242
			}
243
		}
244
	}
245
246
	/**
247
	 * For a given name and url restful routes are created:
248
	 *  - index
249
	 *  - show
250
	 *  - create
251
	 *  - update
252
	 *  - destroy
253
	 *
254
	 * @param array $routes
255
	 */
256
	private function processResources(array $routes): void {
257
		// declaration of all restful actions
258
		$actions = [
259
			['name' => 'index', 'verb' => 'GET', 'on-collection' => true],
260
			['name' => 'show', 'verb' => 'GET'],
261
			['name' => 'create', 'verb' => 'POST', 'on-collection' => true],
262
			['name' => 'update', 'verb' => 'PUT'],
263
			['name' => 'destroy', 'verb' => 'DELETE'],
264
		];
265
266
		$resources = $routes['resources'] ?? [];
267
		foreach ($resources as $resource => $config) {
268
269
			// the url parameter used as id to the resource
270
			foreach($actions as $action) {
271
				$url = $config['url'];
272
				$method = $action['name'];
273
				$verb = strtoupper($action['verb'] ?? 'GET');
274
				$collectionAction = $action['on-collection'] ?? false;
275
				if (!$collectionAction) {
276
					$url .= '/{id}';
277
				}
278
				if (isset($action['url-postfix'])) {
279
					$url .= '/' . $action['url-postfix'];
280
				}
281
282
				$controller = $resource;
283
284
				$controllerName = $this->buildControllerName($controller);
285
				$actionName = $this->buildActionName($method);
286
287
				$routeName = $this->appName . '.' . strtolower($resource) . '.' . strtolower($method);
288
289
				$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

289
				/** @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...
290
					new RouteActionHandler($this->container, $controllerName, $actionName)
291
				);
292
			}
293
		}
294
	}
295
296
	/**
297
	 * Based on a given route name the controller name is generated
298
	 * @param string $controller
299
	 * @return string
300
	 */
301
	private function buildControllerName(string $controller): string {
302
		if (!isset($this->controllerNameCache[$controller])) {
303
			$this->controllerNameCache[$controller] = $this->underScoreToCamelCase(ucfirst($controller)) . 'Controller';
304
		}
305
		return $this->controllerNameCache[$controller];
306
	}
307
308
	/**
309
	 * Based on the action part of the route name the controller method name is generated
310
	 * @param string $action
311
	 * @return string
312
	 */
313
	private function buildActionName(string $action): string {
314
		return $this->underScoreToCamelCase($action);
315
	}
316
317
	/**
318
	 * Underscored strings are converted to camel case strings
319
	 * @param string $str
320
	 * @return string
321
	 */
322
	private function underScoreToCamelCase(string $str): string {
323
		$pattern = '/_[a-z]?/';
324
		return preg_replace_callback(
325
			$pattern,
326
			function ($matches) {
327
				return strtoupper(ltrim($matches[0], '_'));
328
			},
329
			$str);
330
	}
331
}
332