Passed
Push — master ( bd4c9d...9976e4 )
by Christoph
12:46 queued 10s
created

AppConfig::setValue()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 59
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 29
c 1
b 0
f 0
nc 8
nop 3
dl 0
loc 59
rs 8.8337

How to fix   Long Method   

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
 * @copyright Copyright (c) 2017, Joas Schilling <[email protected]>
4
 * @copyright Copyright (c) 2016, ownCloud, Inc.
5
 *
6
 * @author Arthur Schiwon <[email protected]>
7
 * @author Bart Visscher <[email protected]>
8
 * @author Christoph Wurst <[email protected]>
9
 * @author Jakob Sack <[email protected]>
10
 * @author Joas Schilling <[email protected]>
11
 * @author Jörn Friedrich Dreyer <[email protected]>
12
 * @author michaelletzgus <[email protected]>
13
 * @author Morris Jobke <[email protected]>
14
 * @author Robin Appelman <[email protected]>
15
 * @author Robin McCorkell <[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;
34
35
use OC\DB\OracleConnection;
36
use OCP\IAppConfig;
37
use OCP\IConfig;
38
use OCP\IDBConnection;
39
40
/**
41
 * This class provides an easy way for apps to store config values in the
42
 * database.
43
 */
44
class AppConfig implements IAppConfig {
45
46
	/** @var array[] */
47
	protected $sensitiveValues = [
48
		'external' => [
49
			'/^sites$/',
50
		],
51
		'spreed' => [
52
			'/^bridge_bot_password/',
53
			'/^signaling_servers$/',
54
			'/^signaling_ticket_secret$/',
55
			'/^stun_servers$/',
56
			'/^turn_servers$/',
57
			'/^turn_server_secret$/',
58
		],
59
		'theming' => [
60
			'/^imprintUrl$/',
61
			'/^privacyUrl$/',
62
			'/^slogan$/',
63
			'/^url$/',
64
		],
65
		'user_ldap' => [
66
			'/^(s..)?ldap_agent_password$/',
67
		],
68
	];
69
70
	/** @var \OCP\IDBConnection */
71
	protected $conn;
72
73
	/** @var array[] */
74
	private $cache = [];
75
76
	/** @var bool */
77
	private $configLoaded = false;
78
79
	/**
80
	 * @param IDBConnection $conn
81
	 */
82
	public function __construct(IDBConnection $conn) {
83
		$this->conn = $conn;
84
		$this->configLoaded = false;
85
	}
86
87
	/**
88
	 * @param string $app
89
	 * @return array
90
	 */
91
	private function getAppValues($app) {
92
		$this->loadConfigValues();
93
94
		if (isset($this->cache[$app])) {
95
			return $this->cache[$app];
96
		}
97
98
		return [];
99
	}
100
101
	/**
102
	 * Get all apps using the config
103
	 *
104
	 * @return array an array of app ids
105
	 *
106
	 * This function returns a list of all apps that have at least one
107
	 * entry in the appconfig table.
108
	 */
109
	public function getApps() {
110
		$this->loadConfigValues();
111
112
		return $this->getSortedKeys($this->cache);
113
	}
114
115
	/**
116
	 * Get the available keys for an app
117
	 *
118
	 * @param string $app the app we are looking for
119
	 * @return array an array of key names
120
	 *
121
	 * This function gets all keys of an app. Please note that the values are
122
	 * not returned.
123
	 */
124
	public function getKeys($app) {
125
		$this->loadConfigValues();
126
127
		if (isset($this->cache[$app])) {
128
			return $this->getSortedKeys($this->cache[$app]);
129
		}
130
131
		return [];
132
	}
133
134
	public function getSortedKeys($data) {
135
		$keys = array_keys($data);
136
		sort($keys);
137
		return $keys;
138
	}
139
140
	/**
141
	 * Gets the config value
142
	 *
143
	 * @param string $app app
144
	 * @param string $key key
145
	 * @param string $default = null, default value if the key does not exist
146
	 * @return string the value or $default
147
	 *
148
	 * This function gets a value from the appconfig table. If the key does
149
	 * not exist the default value will be returned
150
	 */
151
	public function getValue($app, $key, $default = null) {
152
		$this->loadConfigValues();
153
154
		if ($this->hasKey($app, $key)) {
155
			return $this->cache[$app][$key];
156
		}
157
158
		return $default;
159
	}
160
161
	/**
162
	 * check if a key is set in the appconfig
163
	 *
164
	 * @param string $app
165
	 * @param string $key
166
	 * @return bool
167
	 */
168
	public function hasKey($app, $key) {
169
		$this->loadConfigValues();
170
171
		return isset($this->cache[$app][$key]);
172
	}
173
174
	/**
175
	 * Sets a value. If the key did not exist before it will be created.
176
	 *
177
	 * @param string $app app
178
	 * @param string $key key
179
	 * @param string|float|int $value value
180
	 * @return bool True if the value was inserted or updated, false if the value was the same
181
	 */
182
	public function setValue($app, $key, $value) {
183
		if (!$this->hasKey($app, $key)) {
184
			$inserted = (bool) $this->conn->insertIfNotExist('*PREFIX*appconfig', [
185
				'appid' => $app,
186
				'configkey' => $key,
187
				'configvalue' => $value,
188
			], [
189
				'appid',
190
				'configkey',
191
			]);
192
193
			if ($inserted) {
194
				if (!isset($this->cache[$app])) {
195
					$this->cache[$app] = [];
196
				}
197
198
				$this->cache[$app][$key] = $value;
199
				return true;
200
			}
201
		}
202
203
		$sql = $this->conn->getQueryBuilder();
204
		$sql->update('appconfig')
205
			->set('configvalue', $sql->createNamedParameter($value))
206
			->where($sql->expr()->eq('appid', $sql->createNamedParameter($app)))
207
			->andWhere($sql->expr()->eq('configkey', $sql->createNamedParameter($key)));
208
209
		/*
210
		 * Only limit to the existing value for non-Oracle DBs:
211
		 * http://docs.oracle.com/cd/E11882_01/server.112/e26088/conditions002.htm#i1033286
212
		 * > Large objects (LOBs) are not supported in comparison conditions.
213
		 */
214
		if (!($this->conn instanceof OracleConnection)) {
215
216
			/*
217
			 * Only update the value when it is not the same
218
			 * Note that NULL requires some special handling. Since comparing
219
			 * against null can have special results.
220
			 */
221
222
			if ($value === null) {
0 ignored issues
show
introduced by
The condition $value === null is always false.
Loading history...
223
				$sql->andWhere(
224
					$sql->expr()->isNotNull('configvalue')
225
				);
226
			} else {
227
				$sql->andWhere(
228
					$sql->expr()->orX(
229
						$sql->expr()->isNull('configvalue'),
230
						$sql->expr()->neq('configvalue', $sql->createNamedParameter($value))
231
					)
232
				);
233
			}
234
		}
235
236
		$changedRow = (bool) $sql->execute();
237
238
		$this->cache[$app][$key] = $value;
239
240
		return $changedRow;
241
	}
242
243
	/**
244
	 * Deletes a key
245
	 *
246
	 * @param string $app app
247
	 * @param string $key key
248
	 * @return boolean
249
	 */
250
	public function deleteKey($app, $key) {
251
		$this->loadConfigValues();
252
253
		$sql = $this->conn->getQueryBuilder();
254
		$sql->delete('appconfig')
255
			->where($sql->expr()->eq('appid', $sql->createParameter('app')))
256
			->andWhere($sql->expr()->eq('configkey', $sql->createParameter('configkey')))
257
			->setParameter('app', $app)
258
			->setParameter('configkey', $key);
259
		$sql->execute();
260
261
		unset($this->cache[$app][$key]);
262
		return false;
263
	}
264
265
	/**
266
	 * Remove app from appconfig
267
	 *
268
	 * @param string $app app
269
	 * @return boolean
270
	 *
271
	 * Removes all keys in appconfig belonging to the app.
272
	 */
273
	public function deleteApp($app) {
274
		$this->loadConfigValues();
275
276
		$sql = $this->conn->getQueryBuilder();
277
		$sql->delete('appconfig')
278
			->where($sql->expr()->eq('appid', $sql->createParameter('app')))
279
			->setParameter('app', $app);
280
		$sql->execute();
281
282
		unset($this->cache[$app]);
283
		return false;
284
	}
285
286
	/**
287
	 * get multiple values, either the app or key can be used as wildcard by setting it to false
288
	 *
289
	 * @param string|false $app
290
	 * @param string|false $key
291
	 * @return array|false
292
	 */
293
	public function getValues($app, $key) {
294
		if (($app !== false) === ($key !== false)) {
295
			return false;
296
		}
297
298
		if ($key === false) {
299
			return $this->getAppValues($app);
300
		} else {
301
			$appIds = $this->getApps();
302
			$values = array_map(function ($appId) use ($key) {
303
				return isset($this->cache[$appId][$key]) ? $this->cache[$appId][$key] : null;
304
			}, $appIds);
305
			$result = array_combine($appIds, $values);
306
307
			return array_filter($result);
308
		}
309
	}
310
311
	/**
312
	 * get all values of the app or and filters out sensitive data
313
	 *
314
	 * @param string $app
315
	 * @return array
316
	 */
317
	public function getFilteredValues($app) {
318
		$values = $this->getValues($app, false);
319
320
		if (isset($this->sensitiveValues[$app])) {
321
			foreach ($this->sensitiveValues[$app] as $sensitiveKeyExp) {
322
				$sensitiveKeys = preg_grep($sensitiveKeyExp, array_keys($values));
323
				foreach ($sensitiveKeys as $sensitiveKey) {
324
					$values[$sensitiveKey] = IConfig::SENSITIVE_VALUE;
325
				}
326
			}
327
		}
328
329
		return $values;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $values could also return false which is incompatible with the documented return type array. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
330
	}
331
332
	/**
333
	 * Load all the app config values
334
	 */
335
	protected function loadConfigValues() {
336
		if ($this->configLoaded) {
337
			return;
338
		}
339
340
		$this->cache = [];
341
342
		$sql = $this->conn->getQueryBuilder();
343
		$sql->select('*')
344
			->from('appconfig');
345
		$result = $sql->execute();
346
347
		// we are going to store the result in memory anyway
348
		$rows = $result->fetchAll();
349
		foreach ($rows as $row) {
350
			if (!isset($this->cache[$row['appid']])) {
351
				$this->cache[$row['appid']] = [];
352
			}
353
354
			$this->cache[$row['appid']][$row['configkey']] = $row['configvalue'];
355
		}
356
		$result->closeCursor();
357
358
		$this->configLoaded = true;
359
	}
360
361
	/**
362
	 * Clear all the cached app config values
363
	 *
364
	 * WARNING: do not use this - this is only for usage with the SCSSCacher to
365
	 * clear the memory cache of the app config
366
	 */
367
	public function clearCachedConfig() {
368
		$this->configLoaded = false;
369
	}
370
}
371