Completed
Push — stable8.2 ( 3b3780...cb9d0b )
by Lukas
29s
created

AppConfig::loadConfigValues()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 32
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 32
ccs 15
cts 15
cp 1
rs 8.5806
cc 4
eloc 18
nc 4
nop 0
crap 4
1
<?php
2
/**
3
 * @author Arthur Schiwon <[email protected]>
4
 * @author Bart Visscher <[email protected]>
5
 * @author Jakob Sack <[email protected]>
6
 * @author Joas Schilling <[email protected]>
7
 * @author Jörn Friedrich Dreyer <[email protected]>
8
 * @author Morris Jobke <[email protected]>
9
 * @author Robin Appelman <[email protected]>
10
 * @author Robin McCorkell <[email protected]>
11
 * @author Scrutinizer Auto-Fixer <[email protected]>
12
 *
13
 * @copyright Copyright (c) 2015, ownCloud, Inc.
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;
31
32
use Doctrine\DBAL\Connection;
33
use OCP\IAppConfig;
34
use OCP\IDBConnection;
35
36
/**
37
 * This class provides an easy way for apps to store config values in the
38
 * database.
39
 */
40
class AppConfig implements IAppConfig {
41
	/**
42
	 * @var \OCP\IDBConnection $conn
43
	 */
44
	protected $conn;
45
46
	private $cache = array();
47
48
	/**
49
	 * @param IDBConnection $conn
50 21
	 */
51 21
	public function __construct(IDBConnection $conn) {
52 21
		$this->conn = $conn;
53 21
		$this->configLoaded = false;
0 ignored issues
show
Bug introduced by
The property configLoaded does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
54
	}
55
56
	/**
57
	 * @param string $app
58
	 * @return array
59 1
	 */
60 1
	private function getAppValues($app) {
61
		$this->loadConfigValues();
62 1
63 1
		if (isset($this->cache[$app])) {
64
			return $this->cache[$app];
65
		}
66
67
		return [];
68
	}
69
70
	/**
71
	 * Get all apps using the config
72
	 *
73
	 * @return array an array of app ids
74
	 *
75
	 * This function returns a list of all apps that have at least one
76
	 * entry in the appconfig table.
77 8
	 */
78 8
	public function getApps() {
79
		$this->loadConfigValues();
80 8
81
		return $this->getSortedKeys($this->cache);
82
	}
83
84
	/**
85
	 * Get the available keys for an app
86
	 *
87
	 * @param string $app the app we are looking for
88
	 * @return array an array of key names
89
	 *
90
	 * This function gets all keys of an app. Please note that the values are
91
	 * not returned.
92 1
	 */
93 1
	public function getKeys($app) {
94
		$this->loadConfigValues();
95 1
96 1
		if (isset($this->cache[$app])) {
97
			return $this->getSortedKeys($this->cache[$app]);
98
		}
99
100
		return [];
101
	}
102 9
103 9
	public function getSortedKeys($data) {
104 9
		$keys = array_keys($data);
105 9
		sort($keys);
106
		return $keys;
107
	}
108
109
	/**
110
	 * Gets the config value
111
	 *
112
	 * @param string $app app
113
	 * @param string $key key
114
	 * @param string $default = null, default value if the key does not exist
115
	 * @return string the value or $default
116
	 *
117
	 * This function gets a value from the appconfig table. If the key does
118
	 * not exist the default value will be returned
119 1154
	 */
120 1154
	public function getValue($app, $key, $default = null) {
121
		$this->loadConfigValues();
122 1154
123 743
		if ($this->hasKey($app, $key)) {
124
			return $this->cache[$app][$key];
125
		}
126 1104
127
		return $default;
128
	}
129
130
	/**
131
	 * check if a key is set in the appconfig
132
	 *
133
	 * @param string $app
134
	 * @param string $key
135
	 * @return bool
136 1158
	 */
137 1158
	public function hasKey($app, $key) {
138
		$this->loadConfigValues();
139 1158
140
		return isset($this->cache[$app][$key]);
141
	}
142
143
	/**
144
	 * Sets a value. If the key did not exist before it will be created.
145
	 *
146
	 * @param string $app app
147
	 * @param string $key key
148
	 * @param string $value value
149
	 * @return bool True if the value was inserted or updated, false if the value was the same
150 195
	 */
151 195
	public function setValue($app, $key, $value) {
152 53
		if (!$this->hasKey($app, $key)) {
153 53
			$inserted = (bool) $this->conn->insertIfNotExist('*PREFIX*appconfig', [
154 53
				'appid' => $app,
155 53
				'configkey' => $key,
156 53
				'configvalue' => $value,
157 53
			], [
158 53
				'appid',
159 53
				'configkey',
160
			]);
161 53
162 53 View Code Duplication
			if ($inserted) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
163 4
				if (!isset($this->cache[$app])) {
164 4
					$this->cache[$app] = [];
165
				}
166 53
167 53
				$this->cache[$app][$key] = $value;
168
				return true;
169 2
			}
170
		}
171 183
172 183
		$sql = $this->conn->getQueryBuilder();
173 183
		$sql->update('appconfig')
174 183
			->set('configvalue', $sql->createParameter('configvalue'))
175 183
			->where($sql->expr()->eq('appid', $sql->createParameter('app')))
176 183
			->andWhere($sql->expr()->eq('configkey', $sql->createParameter('configkey')))
177 183
			->setParameter('configvalue', $value)
178 183
			->setParameter('app', $app)
179
			->setParameter('configkey', $key);
180
181
		/*
182
		 * Only limit to the existing value for non-Oracle DBs:
183
		 * http://docs.oracle.com/cd/E11882_01/server.112/e26088/conditions002.htm#i1033286
184
		 * > Large objects (LOBs) are not supported in comparison conditions.
185 183
		 */
186
		if (!($this->conn instanceof \OC\DB\OracleConnection)) {
187 183
			// Only update the value when it is not the same
188 183
			$sql->andWhere($sql->expr()->neq('configvalue', $sql->createParameter('configvalue')))
189 183
				->setParameter('configvalue', $value);
190
		}
191 183
192
		$changedRow = (bool) $sql->execute();
193 183
194
		$this->cache[$app][$key] = $value;
195 183
196
		return $changedRow;
197
	}
198
199
	/**
200
	 * Deletes a key
201
	 *
202
	 * @param string $app app
203
	 * @param string $key key
204
	 * @return boolean|null
205 8
	 */
206 8
	public function deleteKey($app, $key) {
207
		$this->loadConfigValues();
208 8
209 8
		$sql = $this->conn->getQueryBuilder();
210 8
		$sql->delete('appconfig')
211 8
			->where($sql->expr()->eq('appid', $sql->createParameter('app')))
212 8
			->andWhere($sql->expr()->eq('configkey', $sql->createParameter('configkey')))
213 8
			->setParameter('app', $app)
214 8
			->setParameter('configkey', $key);
215
		$sql->execute();
216 8
217 8
		unset($this->cache[$app][$key]);
218
	}
219
220
	/**
221
	 * Remove app from appconfig
222
	 *
223
	 * @param string $app app
224
	 * @return boolean|null
225
	 *
226
	 * Removes all keys in appconfig belonging to the app.
227 1
	 */
228 1
	public function deleteApp($app) {
229
		$this->loadConfigValues();
230 1
231 1
		$sql = $this->conn->getQueryBuilder();
232 1
		$sql->delete('appconfig')
233 1
			->where($sql->expr()->eq('appid', $sql->createParameter('app')))
234 1
			->setParameter('app', $app);
235
		$sql->execute();
236 1
237 1
		unset($this->cache[$app]);
238
	}
239
240
	/**
241
	 * get multiple values, either the app or key can be used as wildcard by setting it to false
242
	 *
243
	 * @param string|false $app
244
	 * @param string|false $key
245
	 * @return array|false
246 8
	 */
247 8
	public function getValues($app, $key) {
248 1
		if (($app !== false) === ($key !== false)) {
249
			return false;
250
		}
251 7
252 1
		if ($key === false) {
253
			return $this->getAppValues($app);
0 ignored issues
show
Bug introduced by
It seems like $app defined by parameter $app on line 247 can also be of type false; however, OC\AppConfig::getAppValues() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
254 7
		} else {
255 7
			$configs = [];
256 7
			foreach ($this->getApps() as $appId) {
257 7
				if ($this->hasKey($appId, $key)) {
258 7
					$configs[$appId] = $this->getValue($appId, $key);
259 7
				}
260
			}
261 7
262
			return $configs;
263
		}
264
	}
265
266
	/**
267
	 * Load all the app config values
268 1160
	 */
269 1160
	protected function loadConfigValues() {
270
		if ($this->configLoaded) return;
271 16
272
		$this->cache = [];
273 16
274 16
		$sql = $this->conn->getQueryBuilder();
275 16
		$sql->select('*')
276 16
			->from('appconfig');
277
		// Note: due to performance issues when there are a lot of shares,
278 16
		// we are not loading the propagation timestamps by default anymore.
279 16
		// The code relying on those values has been adjusted to grab the values
280 16
		// manually. In 9.0 the propagation was changed to not be stored in this
281 16
		// table anymore.
282
		$sql->where($sql->expr()->orX(
283 16
				$sql->expr()->neq('appid', $sql->createParameter('appid')),
284 16
				$sql->expr()->in('configkey', $sql->createParameter('legit_configs'))
0 ignored issues
show
Unused Code introduced by
The call to IExpressionBuilder::orX() has too many arguments starting with $sql->expr()->in('config...meter('legit_configs')).

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
285 16
			))
286
			->setParameter('appid', 'files_sharing', \PDO::PARAM_STR)
287 16
			->setParameter('legit_configs', ['enabled', 'installed_version', 'types'], Connection::PARAM_STR_ARRAY);
288 16
		$result = $sql->execute();
289
290
		while ($row = $result->fetch()) {
291
			if (!isset($this->cache[$row['appid']])) {
292
				$this->cache[$row['appid']] = [];
293
			}
294
295
			$this->cache[$row['appid']][$row['configkey']] = $row['configvalue'];
296
		}
297
		$result->closeCursor();
298
299
		$this->configLoaded = true;
300
	}
301
}
302