These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | namespace Elgg\Database; |
||
3 | |||
4 | use Elgg\Database; |
||
5 | use Elgg\Database\EntityTable; |
||
6 | use Elgg\Cache\PluginSettingsCache; |
||
7 | |||
8 | /** |
||
9 | * Private settings for entities |
||
10 | * |
||
11 | * Private settings provide metadata like storage of settings for plugins |
||
12 | * and users. |
||
13 | * |
||
14 | * WARNING: API IN FLUX. DO NOT USE DIRECTLY. |
||
15 | * |
||
16 | * @access private |
||
17 | * @since 2.0.0 |
||
18 | */ |
||
19 | class PrivateSettingsTable { |
||
20 | |||
21 | /** |
||
22 | * @var Database |
||
23 | */ |
||
24 | protected $db; |
||
25 | |||
26 | /** |
||
27 | * @var EntityTable |
||
28 | */ |
||
29 | protected $entities; |
||
30 | |||
31 | /** |
||
32 | * @var string Name of the database table |
||
33 | */ |
||
34 | protected $table; |
||
35 | |||
36 | /** |
||
37 | * @var PluginSettingsCache cache for settings |
||
38 | */ |
||
39 | protected $cache; |
||
40 | |||
41 | /** |
||
42 | * Constructor |
||
43 | * |
||
44 | * @param Database $db The database |
||
45 | * @param EntityTable $entities Entities table |
||
46 | * @param PluginSettingsCache $cache Settings cache |
||
47 | */ |
||
48 | 196 | public function __construct(Database $db, EntityTable $entities, PluginSettingsCache $cache) { |
|
49 | 196 | $this->db = $db; |
|
50 | 196 | $this->entities = $entities; |
|
51 | 196 | $this->cache = $cache; |
|
52 | 196 | $this->table = $this->db->prefix . 'private_settings'; |
|
53 | 196 | } |
|
54 | |||
55 | /** |
||
56 | * Returns entities based upon private settings |
||
57 | * |
||
58 | * Also accepts all options available to elgg_get_entities(). Supports |
||
59 | * the singular option shortcut. |
||
60 | * |
||
61 | * @param array $options Array in format: |
||
62 | * |
||
63 | * private_setting_names => null|ARR private setting names |
||
64 | * |
||
65 | * private_setting_values => null|ARR metadata values |
||
66 | * |
||
67 | * private_setting_name_value_pairs => null|ARR ( |
||
68 | * name => 'name', |
||
69 | * value => 'value', |
||
70 | * 'operand' => '=', |
||
71 | * ) |
||
72 | * Currently if multiple values are sent via |
||
73 | * an array (value => array('value1', 'value2') |
||
74 | * the pair's operand will be forced to "IN". |
||
75 | * |
||
76 | * private_setting_name_value_pairs_operator => null|STR The operator to |
||
77 | * use for combining |
||
78 | * (name = value) OPERATOR (name = value); |
||
79 | * default AND |
||
80 | * |
||
81 | * private_setting_name_prefix => STR A prefix to apply to all private |
||
82 | * settings. Used to namespace plugin user |
||
83 | * settings or by plugins to namespace their |
||
84 | * own settings. |
||
85 | * |
||
86 | * @return mixed int If count, int. If not count, array. false on errors. |
||
87 | */ |
||
88 | 1 | public function getEntities(array $options = []) { |
|
89 | $defaults = [ |
||
90 | 1 | 'private_setting_names' => ELGG_ENTITIES_ANY_VALUE, |
|
91 | 1 | 'private_setting_values' => ELGG_ENTITIES_ANY_VALUE, |
|
92 | 1 | 'private_setting_name_value_pairs' => ELGG_ENTITIES_ANY_VALUE, |
|
93 | 1 | 'private_setting_name_value_pairs_operator' => 'AND', |
|
94 | 1 | 'private_setting_name_prefix' => '', |
|
95 | ]; |
||
96 | |||
97 | 1 | $options = array_merge($defaults, $options); |
|
98 | |||
99 | $singulars = [ |
||
100 | 1 | 'private_setting_name', |
|
101 | 'private_setting_value', |
||
102 | 'private_setting_name_value_pair', |
||
103 | ]; |
||
104 | |||
105 | 1 | $options = _elgg_normalize_plural_options_array($options, $singulars); |
|
106 | |||
107 | 1 | $clauses = $this->getWhereSql('e', |
|
108 | 1 | $options['private_setting_names'], |
|
109 | 1 | $options['private_setting_values'], |
|
110 | 1 | $options['private_setting_name_value_pairs'], |
|
111 | 1 | $options['private_setting_name_value_pairs_operator'], |
|
112 | 1 | $options['private_setting_name_prefix']); |
|
113 | |||
114 | 1 | View Code Duplication | if ($clauses) { |
115 | // merge wheres to pass to get_entities() |
||
116 | 1 | if (isset($options['wheres']) && !is_array($options['wheres'])) { |
|
117 | $options['wheres'] = [$options['wheres']]; |
||
118 | 1 | } elseif (!isset($options['wheres'])) { |
|
119 | 1 | $options['wheres'] = []; |
|
120 | } |
||
121 | |||
122 | 1 | $options['wheres'] = array_merge($options['wheres'], $clauses['wheres']); |
|
123 | |||
124 | // merge joins to pass to get_entities() |
||
125 | 1 | if (isset($options['joins']) && !is_array($options['joins'])) { |
|
126 | $options['joins'] = [$options['joins']]; |
||
127 | 1 | } elseif (!isset($options['joins'])) { |
|
128 | 1 | $options['joins'] = []; |
|
129 | } |
||
130 | |||
131 | 1 | $options['joins'] = array_merge($options['joins'], $clauses['joins']); |
|
132 | } |
||
133 | |||
134 | 1 | return $this->entities->getEntities($options); |
|
135 | } |
||
136 | |||
137 | /** |
||
138 | * Returns private setting name and value SQL where/join clauses for entities |
||
139 | * |
||
140 | * @param string $table Entities table name |
||
141 | * @param array|null $names Array of names |
||
142 | * @param array|null $values Array of values |
||
143 | * @param array|null $pairs Array of names / values / operands |
||
144 | * @param string $pair_operator Operator for joining pairs where clauses |
||
145 | * @param string $name_prefix A string to prefix all names with |
||
146 | * @return array |
||
147 | */ |
||
148 | 1 | private function getWhereSql($table, $names = null, $values = null, |
|
149 | $pairs = null, $pair_operator = 'AND', $name_prefix = '') { |
||
150 | |||
151 | // @todo short circuit test |
||
152 | |||
153 | $return = [ |
||
154 | 1 | 'joins' => [], |
|
155 | 'wheres' => [], |
||
156 | ]; |
||
157 | |||
158 | 1 | $return['joins'][] = "JOIN {$this->table} ps on |
|
159 | 1 | {$table}.guid = ps.entity_guid"; |
|
160 | |||
161 | 1 | $wheres = []; |
|
162 | |||
163 | // get names wheres |
||
164 | 1 | $names_where = ''; |
|
165 | 1 | View Code Duplication | if ($names !== null) { |
1 ignored issue
–
show
|
|||
166 | if (!is_array($names)) { |
||
167 | $names = [$names]; |
||
168 | } |
||
169 | |||
170 | $sanitised_names = []; |
||
171 | foreach ($names as $name) { |
||
172 | $name = $name_prefix . $name; |
||
173 | $sanitised_names[] = '\'' . $this->db->sanitizeString($name) . '\''; |
||
174 | } |
||
175 | |||
176 | $names_str = implode(',', $sanitised_names); |
||
177 | if ($names_str) { |
||
178 | $names_where = "(ps.name IN ($names_str))"; |
||
179 | } |
||
180 | } |
||
181 | |||
182 | // get values wheres |
||
183 | 1 | $values_where = ''; |
|
184 | 1 | View Code Duplication | if ($values !== null) { |
185 | if (!is_array($values)) { |
||
186 | $values = [$values]; |
||
187 | } |
||
188 | |||
189 | $sanitised_values = []; |
||
190 | foreach ($values as $value) { |
||
191 | // normalize to 0 |
||
192 | if (!$value) { |
||
193 | $value = 0; |
||
194 | } |
||
195 | $sanitised_values[] = '\'' . $this->db->sanitizeString($value) . '\''; |
||
196 | } |
||
197 | |||
198 | $values_str = implode(',', $sanitised_values); |
||
199 | if ($values_str) { |
||
200 | $values_where = "(ps.value IN ($values_str))"; |
||
201 | } |
||
202 | } |
||
203 | |||
204 | 1 | View Code Duplication | if ($names_where && $values_where) { |
205 | $wheres[] = "($names_where AND $values_where)"; |
||
206 | 1 | } elseif ($names_where) { |
|
207 | $wheres[] = "($names_where)"; |
||
208 | 1 | } elseif ($values_where) { |
|
209 | $wheres[] = "($values_where)"; |
||
210 | } |
||
211 | |||
212 | // add pairs which must be in arrays. |
||
213 | 1 | if (is_array($pairs)) { |
|
214 | // join counter for incremental joins in pairs |
||
215 | 1 | $i = 1; |
|
216 | |||
217 | // check if this is an array of pairs or just a single pair. |
||
218 | 1 | if (isset($pairs['name']) || isset($pairs['value'])) { |
|
219 | $pairs = [$pairs]; |
||
220 | } |
||
221 | |||
222 | 1 | $pair_wheres = []; |
|
223 | |||
224 | 1 | foreach ($pairs as $index => $pair) { |
|
225 | // @todo move this elsewhere? |
||
226 | // support shortcut 'n' => 'v' method. |
||
227 | 1 | if (!is_array($pair)) { |
|
228 | $pair = [ |
||
229 | 'name' => $index, |
||
230 | 'value' => $pair |
||
231 | ]; |
||
232 | } |
||
233 | |||
234 | // must have at least a name and value |
||
235 | 1 | if (!isset($pair['name']) || !isset($pair['value'])) { |
|
236 | // @todo should probably return false. |
||
237 | continue; |
||
238 | } |
||
239 | |||
240 | 1 | View Code Duplication | if (isset($pair['operand'])) { |
241 | $operand = $this->db->sanitizeString($pair['operand']); |
||
242 | } else { |
||
243 | 1 | $operand = ' = '; |
|
244 | } |
||
245 | |||
246 | // for comparing |
||
247 | 1 | $trimmed_operand = trim(strtolower($operand)); |
|
248 | |||
249 | // if the value is an int, don't quote it because str '15' < str '5' |
||
250 | // if the operand is IN don't quote it because quoting should be done already. |
||
251 | 1 | if (is_numeric($pair['value'])) { |
|
252 | $value = $this->db->sanitizeString($pair['value']); |
||
253 | 1 | View Code Duplication | } else if (is_array($pair['value'])) { |
254 | $values_array = []; |
||
255 | |||
256 | foreach ($pair['value'] as $pair_value) { |
||
257 | if (is_numeric($pair_value)) { |
||
258 | $values_array[] = $this->db->sanitizeString($pair_value); |
||
259 | } else { |
||
260 | $values_array[] = "'" . $this->db->sanitizeString($pair_value) . "'"; |
||
261 | } |
||
262 | } |
||
263 | |||
264 | if ($values_array) { |
||
265 | $value = '(' . implode(', ', $values_array) . ')'; |
||
266 | } |
||
267 | |||
268 | // @todo allow support for non IN operands with array of values. |
||
269 | // will have to do more silly joins. |
||
270 | $operand = 'IN'; |
||
271 | 1 | } else if ($trimmed_operand == 'in') { |
|
272 | $value = "({$pair['value']})"; |
||
273 | } else { |
||
274 | 1 | $value = "'" . $this->db->sanitizeString($pair['value']) . "'"; |
|
275 | } |
||
276 | |||
277 | 1 | $name = $this->db->sanitizeString($name_prefix . $pair['name']); |
|
278 | |||
279 | // @todo The multiple joins are only needed when the operator is AND |
||
280 | 1 | $return['joins'][] = "JOIN {$this->table} ps{$i} |
|
281 | 1 | on {$table}.guid = ps{$i}.entity_guid"; |
|
282 | |||
283 | 1 | $pair_wheres[] = "(ps{$i}.name = '$name' AND ps{$i}.value |
|
284 | 1 | $operand $value)"; |
|
285 | |||
286 | 1 | $i++; |
|
287 | } |
||
288 | |||
289 | 1 | $where = implode(" $pair_operator ", $pair_wheres); |
|
290 | 1 | if ($where) { |
|
291 | 1 | $wheres[] = "($where)"; |
|
292 | } |
||
293 | } |
||
294 | |||
295 | 1 | $where = implode(' AND ', $wheres); |
|
296 | 1 | if ($where) { |
|
297 | 1 | $return['wheres'][] = "($where)"; |
|
298 | } |
||
299 | |||
300 | 1 | return $return; |
|
301 | } |
||
302 | |||
303 | /** |
||
304 | * Gets a private setting for an entity |
||
305 | * |
||
306 | * Plugin authors can set private data on entities. By default private |
||
307 | * data will not be searched or exported. |
||
308 | * |
||
309 | * @param int $entity_guid The entity GUID |
||
310 | * @param string $name The name of the setting |
||
311 | * |
||
312 | * @return mixed The setting value, or null if does not exist |
||
313 | */ |
||
314 | 5 | public function get($entity_guid, $name) { |
|
315 | |||
316 | 5 | $values = $this->cache->getAll($entity_guid); |
|
317 | 5 | if (isset($values[$name])) { |
|
318 | return $values[$name]; |
||
319 | } |
||
320 | |||
321 | 5 | if (!$this->entities->exists($entity_guid)) { |
|
322 | return false; |
||
323 | } |
||
324 | |||
325 | $query = " |
||
326 | 5 | SELECT value FROM {$this->table} |
|
327 | WHERE name = :name |
||
328 | AND entity_guid = :entity_guid |
||
329 | "; |
||
330 | $params = [ |
||
331 | 5 | ':entity_guid' => (int) $entity_guid, |
|
332 | 5 | ':name' => (string) $name, |
|
333 | ]; |
||
334 | |||
335 | 5 | $setting = $this->db->getDataRow($query, null, $params); |
|
336 | |||
337 | 5 | if ($setting) { |
|
338 | 5 | return $setting->value; |
|
339 | } |
||
340 | |||
341 | 3 | return null; |
|
342 | } |
||
343 | |||
344 | /** |
||
345 | * Return an array of all private settings. |
||
346 | * |
||
347 | * @param int $entity_guid The entity GUID |
||
348 | * |
||
349 | * @return string[] empty array if no settings |
||
350 | */ |
||
351 | public function getAll($entity_guid) { |
||
352 | if (!$this->entities->exists($entity_guid)) { |
||
353 | return []; |
||
354 | } |
||
355 | |||
356 | $query = " |
||
357 | SELECT * FROM {$this->table} |
||
358 | WHERE entity_guid = :entity_guid |
||
359 | "; |
||
360 | $params = [ |
||
361 | ':entity_guid' => (int) $entity_guid, |
||
362 | ]; |
||
363 | |||
364 | $result = $this->db->getData($query, null, $params); |
||
365 | |||
366 | $return = []; |
||
367 | |||
368 | if ($result) { |
||
369 | foreach ($result as $r) { |
||
370 | $return[$r->name] = $r->value; |
||
371 | } |
||
372 | } |
||
373 | |||
374 | return $return; |
||
375 | } |
||
376 | |||
377 | /** |
||
378 | * Sets a private setting for an entity. |
||
379 | * |
||
380 | * @param int $entity_guid The entity GUID |
||
381 | * @param string $name The name of the setting |
||
382 | * @param string $value The value of the setting |
||
383 | * @return bool |
||
384 | */ |
||
385 | 5 | public function set($entity_guid, $name, $value) { |
|
386 | 5 | $this->cache->clear($entity_guid); |
|
387 | 5 | _elgg_services()->boot->invalidateCache(); |
|
388 | |||
389 | 5 | if (!$this->entities->exists($entity_guid)) { |
|
390 | return false; |
||
391 | } |
||
392 | |||
393 | $query = " |
||
394 | 5 | INSERT into {$this->table} |
|
395 | (entity_guid, name, value) VALUES |
||
396 | (:entity_guid, :name, :value) |
||
397 | ON DUPLICATE KEY UPDATE value = :value |
||
398 | "; |
||
399 | $params = [ |
||
400 | 5 | ':entity_guid' => (int) $entity_guid, |
|
401 | 5 | ':name' => (string) $name, |
|
402 | 5 | ':value' => (string) $value, |
|
403 | ]; |
||
404 | |||
405 | 5 | $result = $this->db->insertData($query, $params); |
|
406 | |||
407 | 5 | return $result !== false; |
|
408 | } |
||
409 | |||
410 | /** |
||
411 | * Deletes a private setting for an entity. |
||
412 | * |
||
413 | * @param int $entity_guid The Entity GUID |
||
414 | * @param string $name The name of the setting |
||
415 | * @return bool |
||
416 | */ |
||
417 | View Code Duplication | public function remove($entity_guid, $name) { |
|
418 | $this->cache->clear($entity_guid); |
||
419 | _elgg_services()->boot->invalidateCache(); |
||
420 | |||
421 | $query = " |
||
422 | DELETE FROM {$this->table} |
||
423 | WHERE name = :name |
||
424 | AND entity_guid = :entity_guid |
||
425 | "; |
||
426 | $params = [ |
||
427 | ':entity_guid' => (int) $entity_guid, |
||
428 | ':name' => (string) $name, |
||
429 | ]; |
||
430 | |||
431 | return $this->db->deleteData($query, $params); |
||
432 | } |
||
433 | |||
434 | /** |
||
435 | * Deletes all private settings for an entity |
||
436 | * |
||
437 | * @param int $entity_guid The Entity GUID |
||
438 | * @return bool |
||
439 | */ |
||
440 | View Code Duplication | public function removeAllForEntity($entity_guid) { |
|
441 | $this->cache->clear($entity_guid); |
||
442 | _elgg_services()->boot->invalidateCache(); |
||
443 | |||
444 | $query = " |
||
445 | DELETE FROM {$this->table} |
||
446 | WHERE entity_guid = :entity_guid |
||
447 | "; |
||
448 | $params = [ |
||
449 | ':entity_guid' => (int) $entity_guid, |
||
450 | ]; |
||
451 | |||
452 | return $this->db->deleteData($query, $params); |
||
453 | } |
||
454 | |||
455 | } |
||
456 |
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.