Completed
Push — master ( a84fba...e8b4d4 )
by Thomas
11:12
created

RouteConfig::buildControllerName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Bernhard Posselt <[email protected]>
4
 * @author Morris Jobke <[email protected]>
5
 * @author Patrick Paysant <[email protected]>
6
 * @author Robin Appelman <[email protected]>
7
 * @author Robin McCorkell <[email protected]>
8
 * @author Roeland Jago Douma <[email protected]>
9
 * @author Thomas Müller <[email protected]>
10
 *
11
 * @copyright Copyright (c) 2016, ownCloud GmbH.
12
 * @license AGPL-3.0
13
 *
14
 * This code is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU Affero General Public License, version 3,
16
 * as published by the Free Software Foundation.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
 * GNU Affero General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU Affero General Public License, version 3,
24
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
25
 *
26
 */
27
28
namespace OC\AppFramework\Routing;
29
30
use OC\AppFramework\DependencyInjection\DIContainer;
31
use OCP\Route\IRouter;
32
33
/**
34
 * Class RouteConfig
35
 * @package OC\AppFramework\routing
36
 */
37
class RouteConfig {
38
	/** @var DIContainer */
39
	private $container;
40
	/** @var IRouter */
41
	private $router;
42
	/** @var array */
43
	private $routes;
44
	/** @var string */
45
	private $appName;
46
47
	/**
48
	 * @param \OC\AppFramework\DependencyInjection\DIContainer $container
49
	 * @param \OCP\Route\IRouter $router
50
	 * @param $routes
51
	 * @internal param $appName
52
	 */
53
	public function __construct(DIContainer $container, IRouter $router, $routes) {
54
		$this->routes = $routes;
55
		$this->container = $container;
56
		$this->router = $router;
57
		$this->appName = $container['AppName'];
58
	}
59
60
	/**
61
	 * The routes and resource will be registered to the \OCP\Route\IRouter
62
	 */
63
	public function register() {
64
65
		// parse simple
66
		$this->processSimpleRoutes($this->routes);
67
68
		// parse resources
69
		$this->processResources($this->routes);
70
71
		/*
72
		 * OCS routes go into a different collection
73
		 */
74
		$oldCollection = $this->router->getCurrentCollection();
75
		$this->router->useCollection($oldCollection.'.ocs');
76
77
		// parse ocs simple routes
78
		$this->processOCS($this->routes);
79
80
		$this->router->useCollection($oldCollection);
81
	}
82
83 View Code Duplication
	private function processOCS(array $routes) {
84
		$ocsRoutes = isset($routes['ocs']) ? $routes['ocs'] : [];
85
		foreach ($ocsRoutes as $ocsRoute) {
86
			$name = $ocsRoute['name'];
87
			$postFix = '';
88
89
			if (isset($ocsRoute['postfix'])) {
90
				$postFix = $ocsRoute['postfix'];
91
			}
92
93
			$url = $ocsRoute['url'];
94
			$verb = isset($ocsRoute['verb']) ? strtoupper($ocsRoute['verb']) : 'GET';
95
96
			$split = explode('#', $name, 2);
97
			if (count($split) != 2) {
98
				throw new \UnexpectedValueException('Invalid route name');
99
			}
100
			$controller = $split[0];
101
			$action = $split[1];
102
103
			$controllerName = $this->buildControllerName($controller);
104
			$actionName = $this->buildActionName($action);
105
106
			// register the route
107
			$handler = new RouteActionHandler($this->container, $controllerName, $actionName);
108
109
			$router = $this->router->create('ocs.'.$this->appName.'.'.$controller.'.'.$action . $postFix, $url)
110
				->method($verb)
111
				->action($handler);
112
113
			// optionally register requirements for route. This is used to
114
			// tell the route parser how url parameters should be matched
115
			if(array_key_exists('requirements', $ocsRoute)) {
116
				$router->requirements($ocsRoute['requirements']);
117
			}
118
119
			// optionally register defaults for route. This is used to
120
			// tell the route parser how url parameters should be default valued
121
			if(array_key_exists('defaults', $ocsRoute)) {
122
				$router->defaults($ocsRoute['defaults']);
123
			}
124
		}
125
	}
126
127
	/**
128
	 * Creates one route base on the give configuration
129
	 * @param array $routes
130
	 * @throws \UnexpectedValueException
131
	 */
132 View Code Duplication
	private function processSimpleRoutes($routes)
133
	{
134
		$simpleRoutes = isset($routes['routes']) ? $routes['routes'] : [];
135
		foreach ($simpleRoutes as $simpleRoute) {
136
			$name = $simpleRoute['name'];
137
			$postfix = '';
138
139
			if (isset($simpleRoute['postfix'])) {
140
				$postfix = $simpleRoute['postfix'];
141
			}
142
143
			$url = $simpleRoute['url'];
144
			$verb = isset($simpleRoute['verb']) ? strtoupper($simpleRoute['verb']) : 'GET';
145
146
			$split = explode('#', $name, 2);
147
			if (count($split) != 2) {
148
				throw new \UnexpectedValueException('Invalid route name');
149
			}
150
			$controller = $split[0];
151
			$action = $split[1];
152
153
			$controllerName = $this->buildControllerName($controller);
154
			$actionName = $this->buildActionName($action);
155
156
			// register the route
157
			$handler = new RouteActionHandler($this->container, $controllerName, $actionName);
158
			$router = $this->router->create($this->appName.'.'.$controller.'.'.$action . $postfix, $url)
159
							->method($verb)
160
							->action($handler);
161
162
			// optionally register requirements for route. This is used to
163
			// tell the route parser how url parameters should be matched
164
			if(array_key_exists('requirements', $simpleRoute)) {
165
				$router->requirements($simpleRoute['requirements']);
166
			}
167
168
			// optionally register defaults for route. This is used to
169
			// tell the route parser how url parameters should be default valued
170
			if(array_key_exists('defaults', $simpleRoute)) {
171
				$router->defaults($simpleRoute['defaults']);
172
			}
173
		}
174
	}
175
176
	/**
177
	 * For a given name and url restful routes are created:
178
	 *  - index
179
	 *  - show
180
	 *  - new
181
	 *  - create
182
	 *  - update
183
	 *  - destroy
184
	 *
185
	 * @param array $routes
186
	 */
187
	private function processResources($routes)
188
	{
189
		// declaration of all restful actions
190
		$actions = [
191
			['name' => 'index', 'verb' => 'GET', 'on-collection' => true],
192
			['name' => 'show', 'verb' => 'GET'],
193
			['name' => 'create', 'verb' => 'POST', 'on-collection' => true],
194
			['name' => 'update', 'verb' => 'PUT'],
195
			['name' => 'destroy', 'verb' => 'DELETE'],
196
		];
197
198
		$resources = isset($routes['resources']) ? $routes['resources'] : [];
199
		foreach ($resources as $resource => $config) {
200
201
			// the url parameter used as id to the resource
202
			foreach($actions as $action) {
203
				$url = $config['url'];
204
				$method = $action['name'];
205
				$verb = isset($action['verb']) ? strtoupper($action['verb']) : 'GET';
206
				$collectionAction = isset($action['on-collection']) ? $action['on-collection'] : false;
207
				if (!$collectionAction) {
208
					$url = $url . '/{id}';
209
				}
210
				if (isset($action['url-postfix'])) {
211
					$url = $url . '/' . $action['url-postfix'];
212
				}
213
214
				$controller = $resource;
215
216
				$controllerName = $this->buildControllerName($controller);
217
				$actionName = $this->buildActionName($method);
0 ignored issues
show
Bug introduced by
It seems like $method defined by $action['name'] on line 204 can also be of type boolean; however, OC\AppFramework\Routing\...nfig::buildActionName() 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...
218
219
				$routeName = $this->appName . '.' . strtolower($resource) . '.' . strtolower($method);
220
221
				$this->router->create($routeName, $url)->method($verb)->action(
222
					new RouteActionHandler($this->container, $controllerName, $actionName)
223
				);
224
			}
225
		}
226
	}
227
228
	/**
229
	 * Based on a given route name the controller name is generated
230
	 * @param string $controller
231
	 * @return string
232
	 */
233
	private function buildControllerName($controller)
234
	{
235
		return $this->underScoreToCamelCase(ucfirst($controller)) . 'Controller';
236
	}
237
238
	/**
239
	 * Based on the action part of the route name the controller method name is generated
240
	 * @param string $action
241
	 * @return string
242
	 */
243
	private function buildActionName($action) {
244
		return $this->underScoreToCamelCase($action);
245
	}
246
247
	/**
248
	 * Underscored strings are converted to camel case strings
249
	 * @param string $str
250
	 * @return string
251
	 */
252
	private function underScoreToCamelCase($str) {
253
		$pattern = "/_[a-z]?/";
254
		return preg_replace_callback(
255
			$pattern,
256
			function ($matches) {
257
				return strtoupper(ltrim($matches[0], "_"));
258
			},
259
			$str);
260
	}
261
}
262