Passed
Push — master ( 613f0f...8f650f )
by Roeland
11:57 queued 12s
created

RouteConfig::buildActionName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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

93
		$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...
94
		$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

94
		/** @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...
95
96
		// parse ocs simple routes
97
		$this->processOCS($this->routes);
98
99
		// parse ocs simple routes
100
		$this->processOCSResources($this->routes);
101
102
		$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

102
		/** @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...
103
	}
104
105
	private function processOCS(array $routes): void {
106
		$ocsRoutes = $routes['ocs'] ?? [];
107
		foreach ($ocsRoutes as $ocsRoute) {
108
			$this->processRoute($ocsRoute, 'ocs.');
109
		}
110
	}
111
112
	/**
113
	 * Creates one route base on the give configuration
114
	 * @param array $routes
115
	 * @throws \UnexpectedValueException
116
	 */
117
	private function processSimpleRoutes(array $routes): void {
118
		$simpleRoutes = $routes['routes'] ?? [];
119
		foreach ($simpleRoutes as $simpleRoute) {
120
			$this->processRoute($simpleRoute);
121
		}
122
	}
123
124
	protected function processRoute(array $route, string $routeNamePrefix = ''): void {
125
		$name = $route['name'];
126
		$postfix = $route['postfix'] ?? '';
127
		$defaultRoot = $this->appName === 'core' ? '' : '/apps/' . $this->appName;
128
		$root = $route['root'] ?? $defaultRoot;
129
		if ($routeNamePrefix === '' && !\in_array($this->appName, $this->rootUrlApps, true)) {
130
			// Only allow root URLS for some apps
131
			$root = $defaultRoot;
132
		}
133
134
		$url = $root . $route['url'];
135
		$verb = strtoupper($route['verb'] ?? 'GET');
136
137
		$split = explode('#', $name, 2);
138
		if (count($split) !== 2) {
139
			throw new \UnexpectedValueException('Invalid route name');
140
		}
141
		list($controller, $action) = $split;
142
143
		$controllerName = $this->buildControllerName($controller);
144
		$actionName = $this->buildActionName($action);
145
146
		$routeName = $routeNamePrefix . $this->appName . '.' . $controller . '.' . $action . $postfix;
147
148
		// register the route
149
		$handler = new RouteActionHandler($this->container, $controllerName, $actionName);
150
151
		$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

151
		$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...
152
			->method($verb)
153
			->action($handler);
154
155
		// optionally register requirements for route. This is used to
156
		// tell the route parser how url parameters should be matched
157
		if (array_key_exists('requirements', $route)) {
158
			$router->requirements($route['requirements']);
159
		}
160
161
		// optionally register defaults for route. This is used to
162
		// tell the route parser how url parameters should be default valued
163
		if (array_key_exists('defaults', $route)) {
164
			$router->defaults($route['defaults']);
165
		}
166
	}
167
168
	/**
169
	 * For a given name and url restful OCS routes are created:
170
	 *  - index
171
	 *  - show
172
	 *  - create
173
	 *  - update
174
	 *  - destroy
175
	 *
176
	 * @param array $routes
177
	 */
178
	private function processOCSResources(array $routes): void {
179
		// declaration of all restful actions
180
		$actions = [
181
			['name' => 'index', 'verb' => 'GET', 'on-collection' => true],
182
			['name' => 'show', 'verb' => 'GET'],
183
			['name' => 'create', 'verb' => 'POST', 'on-collection' => true],
184
			['name' => 'update', 'verb' => 'PUT'],
185
			['name' => 'destroy', 'verb' => 'DELETE'],
186
		];
187
188
		$resources = $routes['ocs-resources'] ?? [];
189
		foreach ($resources as $resource => $config) {
190
			$root = $config['root'] ?? '/apps/' . $this->appName;
191
192
			// the url parameter used as id to the resource
193
			foreach ($actions as $action) {
194
				$url = $root . $config['url'];
195
				$method = $action['name'];
196
				$verb = strtoupper($action['verb'] ?? 'GET');
197
				$collectionAction = $action['on-collection'] ?? false;
198
				if (!$collectionAction) {
199
					$url .= '/{id}';
200
				}
201
				if (isset($action['url-postfix'])) {
202
					$url .= '/' . $action['url-postfix'];
203
				}
204
205
				$controller = $resource;
206
207
				$controllerName = $this->buildControllerName($controller);
208
				$actionName = $this->buildActionName($method);
209
210
				$routeName = 'ocs.' . $this->appName . '.' . strtolower($resource) . '.' . strtolower($method);
211
212
				$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

212
				/** @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...
213
					new RouteActionHandler($this->container, $controllerName, $actionName)
214
				);
215
			}
216
		}
217
	}
218
219
	/**
220
	 * For a given name and url restful routes are created:
221
	 *  - index
222
	 *  - show
223
	 *  - create
224
	 *  - update
225
	 *  - destroy
226
	 *
227
	 * @param array $routes
228
	 */
229
	private function processResources(array $routes): void {
230
		// declaration of all restful actions
231
		$actions = [
232
			['name' => 'index', 'verb' => 'GET', 'on-collection' => true],
233
			['name' => 'show', 'verb' => 'GET'],
234
			['name' => 'create', 'verb' => 'POST', 'on-collection' => true],
235
			['name' => 'update', 'verb' => 'PUT'],
236
			['name' => 'destroy', 'verb' => 'DELETE'],
237
		];
238
239
		$resources = $routes['resources'] ?? [];
240
		foreach ($resources as $resource => $config) {
241
242
			// the url parameter used as id to the resource
243
			foreach ($actions as $action) {
244
				$url = $config['url'];
245
				$method = $action['name'];
246
				$verb = strtoupper($action['verb'] ?? 'GET');
247
				$collectionAction = $action['on-collection'] ?? false;
248
				if (!$collectionAction) {
249
					$url .= '/{id}';
250
				}
251
				if (isset($action['url-postfix'])) {
252
					$url .= '/' . $action['url-postfix'];
253
				}
254
255
				$controller = $resource;
256
257
				$controllerName = $this->buildControllerName($controller);
258
				$actionName = $this->buildActionName($method);
259
260
				$routeName = $this->appName . '.' . strtolower($resource) . '.' . strtolower($method);
261
262
				$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

262
				/** @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...
263
					new RouteActionHandler($this->container, $controllerName, $actionName)
264
				);
265
			}
266
		}
267
	}
268
269
	/**
270
	 * Based on a given route name the controller name is generated
271
	 * @param string $controller
272
	 * @return string
273
	 */
274
	private function buildControllerName(string $controller): string {
275
		if (!isset($this->controllerNameCache[$controller])) {
276
			$this->controllerNameCache[$controller] = $this->underScoreToCamelCase(ucfirst($controller)) . 'Controller';
277
		}
278
		return $this->controllerNameCache[$controller];
279
	}
280
281
	/**
282
	 * Based on the action part of the route name the controller method name is generated
283
	 * @param string $action
284
	 * @return string
285
	 */
286
	private function buildActionName(string $action): string {
287
		return $this->underScoreToCamelCase($action);
288
	}
289
290
	/**
291
	 * Underscored strings are converted to camel case strings
292
	 * @param string $str
293
	 * @return string
294
	 */
295
	private function underScoreToCamelCase(string $str): string {
296
		$pattern = '/_[a-z]?/';
297
		return preg_replace_callback(
298
			$pattern,
299
			function ($matches) {
300
				return strtoupper(ltrim($matches[0], '_'));
301
			},
302
			$str);
303
	}
304
}
305