1 | <?php |
||
2 | |||
3 | namespace Elgg\Database; |
||
4 | |||
5 | use DataFormatException; |
||
6 | use Elgg\Config; |
||
7 | use Elgg\Database\Clauses\AnnotationWhereClause; |
||
8 | use Elgg\Database\Clauses\AttributeWhereClause; |
||
9 | use Elgg\Database\Clauses\Clause; |
||
10 | use Elgg\Database\Clauses\EntitySortByClause; |
||
11 | use Elgg\Database\Clauses\GroupByClause; |
||
12 | use Elgg\Database\Clauses\HavingClause; |
||
13 | use Elgg\Database\Clauses\JoinClause; |
||
14 | use Elgg\Database\Clauses\MetadataWhereClause; |
||
15 | use Elgg\Database\Clauses\OrderByClause; |
||
16 | use Elgg\Database\Clauses\PrivateSettingWhereClause; |
||
17 | use Elgg\Database\Clauses\RelationshipWhereClause; |
||
18 | use Elgg\Database\Clauses\SelectClause; |
||
19 | use Elgg\Database\Clauses\WhereClause; |
||
20 | use ElggEntity; |
||
21 | |||
22 | /** |
||
23 | * This trait serves as an adapter between legacy ege* options and new OO query builder |
||
24 | */ |
||
25 | trait LegacyQueryOptionsAdapter { |
||
26 | |||
27 | /** |
||
28 | * Normalizes legacy query options |
||
29 | * |
||
30 | * @param array $options Legacy ege* options |
||
31 | * |
||
32 | * @return array |
||
33 | */ |
||
34 | 1301 | public function normalizeOptions(array $options = []) { |
|
35 | |||
36 | 1301 | if (!isset($options['__original_options'])) { |
|
37 | 1301 | $options['__original_options'] = $options; |
|
38 | } |
||
39 | |||
40 | 1301 | $options = array_merge($this->getDefaults(), $options); |
|
41 | |||
42 | 1301 | _elgg_check_unsupported_site_guid($options); |
|
43 | |||
44 | 1301 | $options = $this->normalizeGuidOptions($options); |
|
45 | 1301 | $options = $this->normalizeTimeOptions($options); |
|
46 | |||
47 | 1301 | $options = $this->normalizeAccessOptions($options); |
|
48 | |||
49 | 1301 | $options = $this->normalizeTypeSubtypeOptions($options); |
|
50 | |||
51 | 1301 | $options = $this->normalizePrivateSettingOptions($options); |
|
52 | 1301 | $options = $this->normalizeRelationshipOptions($options); |
|
53 | 1301 | $options = $this->normalizeAnnotationOptions($options); |
|
54 | 1301 | $options = $this->normalizeMetadataOptions($options); |
|
55 | |||
56 | 1301 | foreach (['selects', 'joins', 'wheres'] as $prop) { |
|
57 | 1301 | if (empty($options[$prop])) { |
|
58 | 1297 | $options[$prop] = []; |
|
59 | } |
||
60 | |||
61 | 1301 | if (!is_array($options[$prop])) { |
|
62 | 215 | if ($options[$prop]) { |
|
63 | 1301 | $options[$prop] = [$options[$prop]]; |
|
64 | } |
||
65 | } |
||
66 | } |
||
67 | |||
68 | 1301 | $options = $this->normalizeSelectClauses($options); |
|
69 | 1301 | $options = $this->normalizeWhereClauses($options); |
|
70 | 1301 | $options = $this->normalizeJoinClauses($options); |
|
71 | 1301 | $options = $this->normalizeOrderByClauses($options); |
|
72 | 1301 | $options = $this->normalizeGroupByClauses($options); |
|
73 | |||
74 | 1301 | return $options; |
|
75 | } |
||
76 | |||
77 | /** |
||
78 | * Returns defaults array |
||
79 | * @return array |
||
80 | */ |
||
81 | 1301 | protected function getDefaults() { |
|
82 | return [ |
||
83 | 1301 | 'types' => null, |
|
84 | 'subtypes' => null, |
||
85 | 'type_subtype_pairs' => null, |
||
86 | 'guids' => null, |
||
87 | 'owner_guids' => null, |
||
88 | 'container_guids' => null, |
||
89 | 'access_ids' => null, |
||
90 | |||
91 | 'created_after' => null, |
||
92 | 'created_before' => null, |
||
93 | 'updated_after' => null, |
||
94 | 'updated_before' => null, |
||
95 | 'last_action_after' => null, |
||
96 | 'last_action_before' => null, |
||
97 | |||
98 | 'sort_by' => [], |
||
99 | 'order_by' => null, |
||
100 | 'count' => false, |
||
101 | 1301 | 'limit' => elgg_get_config('default_limit'), |
|
102 | 1301 | 'offset' => 0, |
|
103 | |||
104 | 'selects' => [], |
||
105 | 'wheres' => [], |
||
106 | 'joins' => [], |
||
107 | 'group_by' => null, |
||
108 | |||
109 | 'metadata_name_value_pairs' => null, |
||
110 | 1301 | 'metadata_name_value_pairs_operator' => 'AND', |
|
111 | 'metadata_case_sensitive' => true, |
||
112 | 'order_by_metadata' => null, |
||
113 | 'metadata_ids' => null, |
||
114 | 'metadata_created_time_lower' => null, |
||
115 | 'metadata_created_time_upper' => null, |
||
116 | 'metadata_calculation' => null, |
||
117 | |||
118 | 'search_name_value_pairs' => null, |
||
119 | |||
120 | 'annotation_names' => null, |
||
121 | 'annotation_values' => null, |
||
122 | 'annotation_name_value_pairs' => null, |
||
123 | 1301 | 'annotation_name_value_pairs_operator' => 'AND', |
|
124 | 'annotation_case_sensitive' => true, |
||
125 | 'order_by_annotation' => null, |
||
126 | 'annotation_ids' => null, |
||
127 | 'annotation_created_time_lower' => null, |
||
128 | 'annotation_created_time_upper' => null, |
||
129 | 'annotation_owner_guids' => null, |
||
130 | 'annotation_calculation' => null, |
||
131 | |||
132 | 'relationship_pairs' => [], |
||
133 | |||
134 | 'relationship' => null, |
||
135 | 'relationship_guid' => null, |
||
136 | 'inverse_relationship' => false, |
||
137 | 1301 | 'relationship_join_on' => 'guid', |
|
138 | 'relationship_created_time_lower' => null, |
||
139 | 'relationship_created_time_upper' => null, |
||
140 | |||
141 | 'private_setting_names' => null, |
||
142 | 'private_setting_values' => null, |
||
143 | 'private_setting_name_value_pairs' => null, |
||
144 | 1301 | 'private_setting_name_value_pairs_operator' => 'AND', |
|
145 | 1301 | 'private_setting_name_prefix' => '', |
|
146 | 'private_setting_case_sensitive' => false, |
||
147 | |||
148 | 'preload_owners' => false, |
||
149 | 'preload_containers' => false, |
||
150 | 'callback' => null, |
||
151 | 'distinct' => true, |
||
152 | |||
153 | 'batch' => false, |
||
154 | 'batch_inc_offset' => true, |
||
155 | 1301 | 'batch_size' => 25, |
|
156 | |||
157 | '__ElggBatch' => null, |
||
158 | ]; |
||
159 | } |
||
160 | |||
161 | /** |
||
162 | * Normalize access options |
||
163 | * |
||
164 | * @param array $options Options |
||
165 | * |
||
166 | * @return array |
||
167 | */ |
||
168 | 1301 | protected function normalizeAccessOptions(array $options = []) { |
|
169 | |||
170 | 1301 | $options = _elgg_normalize_plural_options_array($options, ['access_id']); |
|
171 | |||
172 | 1301 | return $options; |
|
173 | } |
||
174 | |||
175 | /** |
||
176 | * Normalizes type/subtype options |
||
177 | * |
||
178 | * @param array $options Options |
||
179 | * |
||
180 | * @return array |
||
181 | * @throws DataFormatException |
||
182 | */ |
||
183 | 1301 | protected function normalizeTypeSubtypeOptions(array $options = []) { |
|
184 | |||
185 | $singulars = [ |
||
186 | 1301 | 'type', |
|
187 | 'subtype', |
||
188 | ]; |
||
189 | |||
190 | 1301 | $options = _elgg_normalize_plural_options_array($options, $singulars); |
|
191 | |||
192 | // can't use helper function with type_subtype_pair because |
||
193 | // it's already an array...just need to merge it |
||
194 | 1301 | if (isset($options['type_subtype_pair']) && isset($options['type_subtype_pairs'])) { |
|
195 | 1 | $options['type_subtype_pairs'] = array_merge((array) $options['type_subtype_pairs'], (array) $options['type_subtype_pair']); |
|
196 | 1301 | } else if (isset($options['type_subtype_pair'])) { |
|
197 | 2 | $options['type_subtype_pairs'] = (array) $options['type_subtype_pair']; |
|
198 | 1301 | } else if (isset($options['type_subtype_pairs'])) { |
|
199 | 74 | $options['type_subtype_pairs'] = (array) $options['type_subtype_pairs']; |
|
200 | 1301 | } else if (isset($options['types'])) { |
|
201 | 1161 | $options['type_subtype_pairs'] = []; |
|
202 | 1161 | if ($options['types']) { |
|
203 | 1161 | foreach ((array) $options['types'] as $type) { |
|
204 | 1161 | $options['type_subtype_pairs'][$type] = isset($options['subtypes']) ? (array) $options['subtypes'] : null; |
|
205 | } |
||
206 | } |
||
207 | } |
||
208 | |||
209 | 1301 | if (is_array($options['type_subtype_pairs'])) { |
|
210 | 1166 | foreach ($options['type_subtype_pairs'] as $type => $subtypes) { |
|
211 | 1166 | if (!in_array($type, Config::getEntityTypes())) { |
|
212 | 16 | elgg_log("'$type' is not a valid entity type", 'WARNING'); |
|
213 | } |
||
214 | 1166 | if (!empty($subtypes) && !is_array($subtypes)) { |
|
215 | 1166 | $options['type_subtype_pairs'][$type] = [$subtypes]; |
|
216 | } |
||
217 | } |
||
218 | } |
||
219 | |||
220 | 1301 | unset($options['type_subtype_pair']); |
|
221 | 1301 | unset($options['types']); |
|
222 | 1301 | unset($options['subtypes']); |
|
223 | |||
224 | 1301 | return $options; |
|
225 | } |
||
226 | |||
227 | /** |
||
228 | * Normalizes metadata options |
||
229 | * |
||
230 | * @param array $options Options |
||
231 | * |
||
232 | * @return array |
||
233 | */ |
||
234 | 1301 | protected function normalizeMetadataOptions(array $options = []) { |
|
235 | $singulars = [ |
||
236 | 1301 | 'metadata_id', |
|
237 | 'metadata_name', |
||
238 | 'metadata_value', |
||
239 | 'metadata_name_value_pair', |
||
240 | ]; |
||
241 | |||
242 | 1301 | $options = _elgg_normalize_plural_options_array($options, $singulars); |
|
243 | |||
244 | 1301 | $options = $this->normalizePairedOptions('metadata', $options); |
|
245 | 1301 | $options = $this->normalizePairedOptions('search', $options); |
|
246 | |||
247 | 1301 | if (isset($options['order_by_metadata'])) { |
|
248 | 7 | $name = elgg_extract('name', $options['order_by_metadata']); |
|
249 | 7 | $direction = strtoupper(elgg_extract('direction', $options['order_by_metadata'], 'asc')); |
|
250 | 7 | $as = elgg_extract('as', $options['order_by_metadata']); |
|
251 | |||
252 | 7 | if ($name) { |
|
253 | 7 | $options['sort_by'][] = [ |
|
254 | 7 | 'property' => $name, |
|
255 | 7 | 'direction' => in_array($direction, ['ASC', 'DESC']) ? $direction : null, |
|
256 | 7 | 'signed' => $as === ELGG_VALUE_INTEGER, |
|
257 | 7 | 'property_type' => 'metadata', |
|
258 | ]; |
||
259 | } |
||
260 | |||
261 | 7 | $options['order_by'] = null; |
|
262 | 7 | $options['order_by_metadata'] = null; |
|
263 | } |
||
264 | |||
265 | $props = [ |
||
266 | 1301 | 'metadata_ids', |
|
267 | 'metadata_created_after', |
||
268 | 'metadata_created_before', |
||
269 | ]; |
||
270 | |||
271 | 1301 | foreach ($props as $prop) { |
|
272 | 1301 | if (isset($options[$prop]) && empty($options['metadata_name_value_pairs'])) { |
|
273 | $options['metadata_name_value_pairs'][] = [ |
||
274 | 1301 | $prop => $options[$prop] |
|
275 | ]; |
||
276 | } |
||
277 | } |
||
278 | |||
279 | 1301 | foreach ($options['metadata_name_value_pairs'] as $key => $pair) { |
|
280 | 1164 | if ($pair instanceof Clause) { |
|
281 | 66 | continue; |
|
282 | } |
||
283 | |||
284 | 1164 | foreach ($props as $prop) { |
|
285 | 1164 | if (!isset($pair[$prop])) { |
|
286 | 1164 | $options['metadata_name_value_pairs'][$key][$prop] = elgg_extract($prop, $options); |
|
287 | } |
||
288 | } |
||
289 | |||
290 | 1164 | $options['metadata_name_value_pairs'][$key]['entity_guids'] = $options['guids']; |
|
291 | } |
||
292 | |||
293 | 1301 | $options['metadata_name_value_pairs'] = $this->removeKeyPrefix('metadata_', $options['metadata_name_value_pairs']); |
|
294 | |||
295 | $defaults = [ |
||
296 | 1301 | 'name' => null, |
|
297 | 'value' => null, |
||
298 | 1301 | 'comparison' => '=', |
|
299 | 1301 | 'type' => ELGG_VALUE_STRING, |
|
300 | 'case_sensitive' => true, |
||
301 | 'entity_guids' => null, |
||
302 | 'ids' => null, |
||
303 | 'created_after' => null, |
||
304 | 'created_before' => null, |
||
305 | 'entity_guids' => null, |
||
306 | ]; |
||
307 | |||
308 | 1301 | foreach ($options['metadata_name_value_pairs'] as $key => $pair) { |
|
309 | 1164 | if ($pair instanceof WhereClause) { |
|
310 | 66 | continue; |
|
311 | } |
||
312 | |||
313 | 1164 | $pair = array_merge($defaults, $pair); |
|
314 | |||
315 | 1164 | if (in_array(strtolower($pair['comparison']), ['in', 'eq', '=']) && is_string($pair['value'])) { |
|
316 | // Apparently this madness is supported |
||
317 | // \Elgg\Integration\ElggCoreGetEntitiesFromMetadataTest::testElggApiGettersEntityMetadataNVPValidNValidVOperandIn |
||
318 | 999 | $pair['value'] = array_map(function ($e) { |
|
319 | 999 | return trim($e, ' \"\''); |
|
320 | 999 | }, explode(',', $pair['value'])); |
|
321 | } |
||
322 | |||
323 | 1164 | if (in_array($pair['name'], ElggEntity::$primary_attr_names)) { |
|
324 | 9 | $clause = new AttributeWhereClause(); |
|
325 | } else { |
||
326 | 1164 | $clause = new MetadataWhereClause(); |
|
327 | 1164 | $clause->ids = (array) $pair['ids']; |
|
328 | 1164 | $clause->entity_guids = (array) $pair['entity_guids']; |
|
329 | 1164 | $clause->created_after = $pair['created_after']; |
|
330 | 1164 | $clause->created_before = $pair['created_before']; |
|
331 | } |
||
332 | |||
333 | 1164 | $clause->names = (array) $pair['name']; |
|
334 | 1164 | $clause->values = (array) $pair['value']; |
|
335 | 1164 | $clause->comparison = $pair['comparison']; |
|
336 | 1164 | $clause->value_type = $pair['type']; |
|
337 | 1164 | $clause->case_sensitive = $pair['case_sensitive']; |
|
338 | |||
339 | 1164 | $options['metadata_name_value_pairs'][$key] = $clause; |
|
340 | } |
||
341 | |||
342 | 1301 | return $options; |
|
343 | } |
||
344 | |||
345 | /** |
||
346 | * Normalizes annotation options |
||
347 | * |
||
348 | * @param array $options Options |
||
349 | * |
||
350 | * @return array |
||
351 | */ |
||
352 | 1301 | protected function normalizeAnnotationOptions(array $options = []) { |
|
353 | $singulars = [ |
||
354 | 1301 | 'annotation_id', |
|
355 | 'annotation_name', |
||
356 | 'annotation_value', |
||
357 | 'annotation_name_value_pair', |
||
358 | ]; |
||
359 | |||
360 | 1301 | $options = _elgg_normalize_plural_options_array($options, $singulars); |
|
361 | |||
362 | 1301 | $options = $this->normalizePairedOptions('annotation', $options); |
|
363 | |||
364 | 1301 | if (isset($options['order_by_annotation'])) { |
|
365 | 1 | $name = elgg_extract('name', $options['order_by_annotation']); |
|
366 | 1 | $direction = strtoupper(elgg_extract('direction', $options['order_by_annotation'], 'asc')); |
|
367 | 1 | $as = elgg_extract('as', $options['order_by_annotation']); |
|
368 | |||
369 | 1 | if ($name) { |
|
370 | 1 | $options['sort_by'][] = [ |
|
371 | 1 | 'property' => $name, |
|
372 | 1 | 'property_type' => 'annotation', |
|
373 | 1 | 'direction' => in_array($direction, ['ASC', 'DESC']) ? $direction : null, |
|
374 | 1 | 'signed' => $as === ELGG_VALUE_INTEGER, |
|
375 | ]; |
||
376 | } |
||
377 | |||
378 | 1 | $options['order_by'] = null; |
|
379 | 1 | $options['order_by_annotation'] = null; |
|
380 | } |
||
381 | |||
382 | $props = [ |
||
383 | 1301 | 'annotation_ids', |
|
384 | 'annotation_owner_guids', |
||
385 | 'annotation_created_after', |
||
386 | 'annotation_created_before', |
||
387 | 'annotation_sort_by_calculation', |
||
388 | ]; |
||
389 | |||
390 | 1301 | foreach ($props as $prop) { |
|
391 | 1301 | if (isset($options[$prop]) && empty($options['annotation_name_value_pairs'])) { |
|
392 | 215 | $options['annotation_name_value_pairs'][] = [ |
|
393 | 1301 | $prop => $options[$prop] |
|
394 | ]; |
||
395 | } |
||
396 | } |
||
397 | |||
398 | 1301 | foreach ($options['annotation_name_value_pairs'] as $key => $pair) { |
|
399 | 257 | if ($pair instanceof WhereClause) { |
|
400 | 217 | continue; |
|
401 | } |
||
402 | |||
403 | 257 | foreach ($props as $prop) { |
|
404 | 257 | if (!isset($pair[$prop])) { |
|
405 | 257 | $options['annotation_name_value_pairs'][$key][$prop] = elgg_extract($prop, $options); |
|
406 | } |
||
407 | } |
||
408 | |||
409 | 257 | $options['annotation_name_value_pairs'][$key]['entity_guids'] = $options['guids']; |
|
410 | } |
||
411 | |||
412 | 1301 | $options['annotation_name_value_pairs'] = $this->removeKeyPrefix('annotation_', $options['annotation_name_value_pairs']); |
|
413 | |||
414 | $defaults = [ |
||
415 | 1301 | 'name' => null, |
|
416 | 'value' => null, |
||
417 | 1301 | 'comparison' => '=', |
|
418 | 1301 | 'type' => ELGG_VALUE_STRING, |
|
419 | 'case_sensitive' => true, |
||
420 | 'entity_guids' => null, |
||
421 | 'owner_guids' => null, |
||
422 | 'ids' => null, |
||
423 | 'enabled' => null, |
||
424 | 'access_ids' => null, |
||
425 | 'created_after' => null, |
||
426 | 'created_before' => null, |
||
427 | 'sort_by_calculation' => null, |
||
428 | ]; |
||
429 | |||
430 | 1301 | foreach ($options['annotation_name_value_pairs'] as $key => $pair) { |
|
431 | 257 | if ($pair instanceof WhereClause) { |
|
432 | 217 | continue; |
|
433 | } |
||
434 | |||
435 | 257 | $pair = array_merge($defaults, $pair); |
|
436 | |||
437 | 257 | $clause = new AnnotationWhereClause(); |
|
438 | 257 | $clause->ids = (array) $pair['ids']; |
|
439 | 257 | $clause->entity_guids = (array) $pair['entity_guids']; |
|
440 | 257 | $clause->owner_guids = (array) $pair['owner_guids']; |
|
441 | 257 | $clause->created_after = $pair['created_after']; |
|
442 | 257 | $clause->created_before = $pair['created_before']; |
|
443 | 257 | $clause->names = (array) $pair['name']; |
|
444 | 257 | $clause->values = (array) $pair['value']; |
|
445 | 257 | $clause->comparison = $pair['comparison']; |
|
446 | 257 | $clause->value_type = $pair['type']; |
|
447 | 257 | $clause->case_sensitive = $pair['case_sensitive']; |
|
448 | 257 | $clause->enabled = $pair['enabled']; |
|
449 | 257 | $clause->access_ids = (array) $pair['access_ids']; |
|
450 | 257 | $clause->sort_by_calculation = $pair['sort_by_calculation']; |
|
451 | |||
452 | 257 | if ($clause->sort_by_calculation && empty($options['order_by'])) { |
|
453 | 7 | $clause->sort_by_direction = 'desc'; |
|
454 | } |
||
455 | |||
456 | 257 | $options['annotation_name_value_pairs'][$key] = $clause; |
|
457 | } |
||
458 | |||
459 | 1301 | return $options; |
|
460 | } |
||
461 | |||
462 | /** |
||
463 | * Normalizes private settings options |
||
464 | * |
||
465 | * @param array $options Options |
||
466 | * |
||
467 | * @return array |
||
468 | */ |
||
469 | 1301 | protected function normalizePrivateSettingOptions(array $options = []) { |
|
470 | |||
471 | $singulars = [ |
||
472 | 1301 | 'private_setting_name', |
|
473 | 'private_setting_value', |
||
474 | 'private_setting_name_value_pair', |
||
475 | ]; |
||
476 | |||
477 | 1301 | $options = _elgg_normalize_plural_options_array($options, $singulars); |
|
478 | |||
479 | 1301 | if (isset($options['private_setting_name_prefix'])) { |
|
480 | 1301 | $prefix = $options['private_setting_name_prefix']; |
|
481 | 1301 | unset($options['private_setting_name_prefix']); |
|
482 | |||
483 | 1301 | if (is_array($options['private_setting_names'])) { |
|
484 | 78 | $options['private_setting_names'] = array_map(function ($el) use ($prefix) { |
|
485 | 78 | return "$prefix$el"; |
|
486 | 78 | }, $options['private_setting_names']); |
|
487 | } |
||
488 | } |
||
489 | |||
490 | 1301 | $options = $this->normalizePairedOptions('private_setting', $options); |
|
491 | |||
492 | 1301 | foreach ($options['private_setting_name_value_pairs'] as $key => $pair) { |
|
493 | 89 | if ($pair instanceof WhereClause) { |
|
494 | 63 | continue; |
|
495 | } |
||
496 | |||
497 | 89 | $options['private_setting_name_value_pairs'][$key]['entity_guids'] = $options['guids']; |
|
498 | } |
||
499 | |||
500 | 1301 | $options['private_setting_name_value_pairs'] = $this->removeKeyPrefix('private_setting_', $options['private_setting_name_value_pairs']); |
|
501 | |||
502 | $defaults = [ |
||
503 | 1301 | 'name' => null, |
|
504 | 'value' => null, |
||
505 | 1301 | 'comparison' => '=', |
|
506 | 1301 | 'type' => ELGG_VALUE_STRING, |
|
507 | 'case_sensitive' => true, |
||
508 | 'entity_guids' => null, |
||
509 | 'ids' => null, |
||
510 | ]; |
||
511 | |||
512 | 1301 | foreach ($options['private_setting_name_value_pairs'] as $key => $pair) { |
|
513 | 89 | if ($pair instanceof WhereClause) { |
|
514 | 63 | continue; |
|
515 | } |
||
516 | |||
517 | 89 | $pair = array_merge($defaults, $pair); |
|
518 | |||
519 | 89 | $clause = new PrivateSettingWhereClause(); |
|
520 | 89 | $clause->ids = (array) $pair['ids']; |
|
521 | 89 | $clause->entity_guids = (array) $pair['entity_guids']; |
|
522 | 89 | $clause->names = (array) $pair['name']; |
|
523 | 89 | $clause->values = (array) $pair['value']; |
|
524 | 89 | $clause->comparison = $pair['comparison']; |
|
525 | 89 | $clause->value_type = $pair['type']; |
|
526 | 89 | $clause->case_sensitive = $pair['case_sensitive']; |
|
527 | |||
528 | 89 | $options['private_setting_name_value_pairs'][$key] = $clause; |
|
529 | } |
||
530 | |||
531 | 1301 | return $options; |
|
532 | } |
||
533 | |||
534 | /** |
||
535 | * Normalizes paired options |
||
536 | * |
||
537 | * @param string $type Pair type |
||
538 | * @param array $options Options |
||
539 | * |
||
540 | * @return array |
||
541 | */ |
||
542 | 1301 | protected function normalizePairedOptions($type = 'metadata', array $options = []) { |
|
543 | 1301 | if (!is_array($options["{$type}_name_value_pairs"])) { |
|
544 | 1301 | $options["{$type}_name_value_pairs"] = []; |
|
545 | } |
||
546 | |||
547 | 1301 | if (isset($options["{$type}_name_value_pairs"]['name'])) { |
|
548 | 356 | $options["{$type}_name_value_pairs"][] = [ |
|
549 | 356 | 'name' => $options["{$type}_name_value_pairs"]['name'], |
|
550 | 356 | 'value' => elgg_extract('value', $options["{$type}_name_value_pairs"]), |
|
551 | 356 | 'comparison' => elgg_extract('operand', $options["{$type}_name_value_pairs"], '='), |
|
552 | 356 | 'case_sensitive' => elgg_extract('case_sensitive', $options["{$type}_name_value_pairs"], true) |
|
553 | ]; |
||
554 | 356 | unset($options["{$type}_name_value_pairs"]['name']); |
|
555 | 356 | unset($options["{$type}_name_value_pairs"]['value']); |
|
556 | 356 | unset($options["{$type}_name_value_pairs"]['operand']); |
|
557 | 356 | unset($options["{$type}_name_value_pairs"]['case_sensitive']); |
|
558 | } |
||
559 | |||
560 | 1301 | foreach ($options["{$type}_name_value_pairs"] as $index => $pair) { |
|
561 | 1041 | if (is_array($pair)) { |
|
562 | 815 | $keys = array_keys($pair); |
|
563 | 815 | if (sizeof($keys) === 1 && is_string($keys[0])) { |
|
0 ignored issues
–
show
|
|||
564 | 2 | $options["{$type}_name_value_pairs"][$index] = [ |
|
565 | 2 | 'name' => $keys[0], |
|
566 | 2 | 'value' => $pair[$keys[0]], |
|
567 | 1041 | 'comparison' => '=', |
|
568 | ]; |
||
569 | } |
||
570 | } |
||
571 | } |
||
572 | |||
573 | 1301 | foreach ($options["{$type}_name_value_pairs"] as $index => $values) { |
|
574 | 1041 | if ($values instanceof Clause) { |
|
575 | 301 | continue; |
|
576 | } |
||
577 | |||
578 | 1029 | if (is_array($values)) { |
|
579 | 815 | if (isset($values['name'])) { |
|
580 | 811 | continue; |
|
581 | } |
||
582 | } |
||
583 | 363 | $options["{$type}_name_value_pairs"][$index] = [ |
|
584 | 363 | 'name' => $index, |
|
585 | 363 | 'value' => $values, |
|
586 | 363 | 'comparison' => '=', |
|
587 | ]; |
||
588 | } |
||
589 | |||
590 | 1301 | if (isset($options["{$type}_names"]) || isset($options["{$type}_values"])) { |
|
591 | 544 | $options["{$type}_name_value_pairs"][] = [ |
|
592 | 544 | 'name' => isset($options["{$type}_names"]) ? (array) $options["{$type}_names"] : null, |
|
593 | 544 | 'value' => isset($options["{$type}_values"]) ? (array) $options["{$type}_values"] : null, |
|
594 | 544 | 'comparison' => '=', |
|
595 | ]; |
||
596 | } |
||
597 | |||
598 | 1301 | foreach ($options["{$type}_name_value_pairs"] as $key => $value) { |
|
599 | 1208 | if ($value instanceof Clause) { |
|
600 | 301 | continue; |
|
601 | } |
||
602 | |||
603 | 1208 | if (!isset($value['case_sensitive'])) { |
|
604 | 940 | $options["{$type}_name_value_pairs"][$key]['case_sensitive'] = elgg_extract("{$type}_case_sensitive", $options, true); |
|
605 | } |
||
606 | 1208 | if (!isset($value['type'])) { |
|
607 | 1208 | if (is_bool($value['value'])) { |
|
608 | 4 | $value['value'] = (int) $value['value']; |
|
609 | } |
||
610 | 1208 | if (is_int($value['value'])) { |
|
611 | 84 | $options["{$type}_name_value_pairs"][$key]['type'] = ELGG_VALUE_INTEGER; |
|
612 | } else { |
||
613 | 1207 | $options["{$type}_name_value_pairs"][$key]['type'] = ELGG_VALUE_STRING; |
|
614 | } |
||
615 | } |
||
616 | 1208 | if (!isset($value['comparison']) && isset($value['operand'])) { |
|
617 | 5 | $options["{$type}_name_value_pairs"][$key]['comparison'] = $options["{$type}_name_value_pairs"][$key]['operand']; |
|
618 | 1208 | unset($options["{$type}_name_value_pairs"][$key]['operand']); |
|
619 | } |
||
620 | } |
||
621 | |||
622 | 1301 | unset($options["{$type}_names"]); |
|
623 | 1301 | unset($options["{$type}_values"]); |
|
624 | 1301 | unset($options["{$type}_case_sensitive"]); |
|
625 | |||
626 | 1301 | return $options; |
|
627 | } |
||
628 | |||
629 | /** |
||
630 | * Normalizes relationship options |
||
631 | * |
||
632 | * @param array $options Options |
||
633 | * |
||
634 | * @return array |
||
635 | */ |
||
636 | 1301 | protected function normalizeRelationshipOptions(array $options = []) { |
|
637 | |||
638 | 1301 | $pair = []; |
|
639 | |||
640 | $defaults = [ |
||
641 | 1301 | 'relationship_ids' => null, |
|
642 | 'relationship' => null, |
||
643 | 'relationship_guid' => null, |
||
644 | 'inverse_relationship' => false, |
||
645 | 'relationship_join_on' => 'guid', |
||
646 | 'relationship_created_after' => null, |
||
647 | 'relationship_created_before' => null, |
||
648 | ]; |
||
649 | |||
650 | 1301 | foreach (array_keys($defaults) as $prop) { |
|
651 | 1301 | if (isset($options[$prop])) { |
|
652 | 1301 | $pair[$prop] = $options[$prop]; |
|
653 | } |
||
654 | 1301 | unset($options[$prop]); |
|
655 | } |
||
656 | |||
657 | 1301 | $options['relationship_pairs'] = (array) $options['relationship_pairs']; |
|
658 | 1301 | $options['relationship_pairs'][] = $pair; |
|
659 | |||
660 | 1301 | foreach ($options['relationship_pairs'] as $index => $relationship_pair) { |
|
661 | 1301 | if ($relationship_pair instanceof WhereClause) { |
|
662 | 1 | continue; |
|
663 | } |
||
664 | |||
665 | 1301 | $options['relationship_pairs'][$index] = array_merge($defaults, $relationship_pair); |
|
666 | } |
||
667 | |||
668 | 1301 | $options['relationship_pairs'] = $this->removeKeyPrefix('relationship_', $options['relationship_pairs']); |
|
669 | |||
670 | 1301 | foreach ($options['relationship_pairs'] as $key => $pair) { |
|
671 | 1301 | if ($pair instanceof WhereClause) { |
|
672 | 1 | continue; |
|
673 | } |
||
674 | |||
675 | 1301 | $pair = array_merge($defaults, $pair); |
|
676 | |||
677 | 1301 | if (!$pair['relationship'] && !$pair['relationship_guid']) { |
|
678 | 1297 | unset($options['relationship_pairs'][$key]); |
|
679 | 1297 | continue; |
|
680 | } |
||
681 | |||
682 | 35 | $clause = new RelationshipWhereClause(); |
|
683 | 35 | $clause->ids = (array) $pair['ids']; |
|
684 | 35 | $clause->names = (array) $pair['relationship']; |
|
685 | |||
686 | 35 | $clause->join_on = $pair['join_on']; |
|
687 | 35 | $clause->inverse = $pair['inverse_relationship']; |
|
688 | 35 | if ($clause->inverse) { |
|
689 | 25 | $clause->object_guids = (array) $pair['guid']; |
|
690 | } else { |
||
691 | 15 | $clause->subject_guids = (array) $pair['guid']; |
|
692 | } |
||
693 | 35 | $clause->created_after = $pair['created_after']; |
|
694 | 35 | $clause->created_before = $pair['created_before']; |
|
695 | |||
696 | |||
697 | 35 | $options['relationship_pairs'][$key] = $clause; |
|
698 | } |
||
699 | |||
700 | 1301 | return $options; |
|
701 | } |
||
702 | |||
703 | /** |
||
704 | * Normalizes guid based options |
||
705 | * |
||
706 | * @param array $options Options |
||
707 | * |
||
708 | * @return array |
||
709 | */ |
||
710 | 1301 | protected function normalizeGuidOptions(array $options = []) { |
|
711 | |||
712 | $singulars = [ |
||
713 | 1301 | 'guid', |
|
714 | 'owner_guid', |
||
715 | 'container_guid', |
||
716 | 'annotation_owner_guid', |
||
717 | ]; |
||
718 | |||
719 | 1301 | $options = _elgg_normalize_plural_options_array($options, $singulars); |
|
720 | |||
721 | $names = [ |
||
722 | 1301 | 'guids', |
|
723 | 'owner_guids', |
||
724 | 'container_guids', |
||
725 | 'annotation_owner_guids', |
||
726 | 'relationship_guid', |
||
727 | ]; |
||
728 | |||
729 | 1301 | foreach ($names as $name) { |
|
730 | 1301 | $options[$name] = !empty($options[$name]) ? $options[$name] : null; |
|
731 | } |
||
732 | |||
733 | 1301 | return $options; |
|
734 | } |
||
735 | |||
736 | /** |
||
737 | * Normalizes time based options |
||
738 | * |
||
739 | * @param array $options Options array |
||
740 | * |
||
741 | * @return array |
||
742 | */ |
||
743 | 1301 | protected function normalizeTimeOptions(array $options = []) { |
|
744 | |||
745 | $props = [ |
||
746 | 1301 | 'modified', |
|
747 | 'created', |
||
748 | 'updated', |
||
749 | 'metadata_created', |
||
750 | 'annotation_created', |
||
751 | 'relationship_created', |
||
752 | 'last_action', |
||
753 | 'posted', |
||
754 | ]; |
||
755 | |||
756 | 1301 | $bounds = ['time_lower', 'time_upper', 'after', 'before']; |
|
757 | |||
758 | 1301 | foreach ($props as $prop) { |
|
759 | 1301 | foreach ($bounds as $bound) { |
|
760 | 1301 | $prop_name = "{$prop}_{$bound}"; |
|
761 | |||
762 | 1301 | $time = elgg_extract($prop_name, $options); |
|
0 ignored issues
–
show
|
|||
763 | |||
764 | 1301 | $new_prop_name = $prop_name; |
|
765 | 1301 | $new_prop_name = str_replace('modified', 'updated', $new_prop_name); |
|
766 | 1301 | $new_prop_name = str_replace('posted', 'created', $new_prop_name); |
|
767 | 1301 | $new_prop_name = str_replace('time_lower', 'after', $new_prop_name); |
|
768 | 1301 | $new_prop_name = str_replace('time_upper', 'before', $new_prop_name); |
|
769 | |||
770 | 1301 | if (!isset($options[$new_prop_name])) { |
|
771 | 1301 | $options[$new_prop_name] = elgg_extract($prop_name, $options); |
|
772 | } |
||
773 | } |
||
774 | } |
||
775 | |||
776 | 1301 | return $options; |
|
777 | } |
||
778 | |||
779 | /** |
||
780 | * Remove $prefix from array keys |
||
781 | * |
||
782 | * @param string $prefix Prefix |
||
783 | * @param array $array Array |
||
784 | * |
||
785 | * @return array |
||
786 | */ |
||
787 | 1301 | protected function removeKeyPrefix($prefix, array $array = []) { |
|
788 | 1301 | foreach ($array as $key => $value) { |
|
789 | 1301 | $new_key = $key; |
|
790 | 1301 | if (strpos($key, $prefix) === 0) { |
|
791 | 1301 | $new_key = substr($key, strlen($prefix)); |
|
792 | } |
||
793 | 1301 | if (!isset($array[$new_key])) { |
|
794 | 1301 | $array[$new_key] = $array[$key]; |
|
795 | } |
||
796 | 1301 | if ($new_key !== $key) { |
|
797 | 1301 | unset($array[$key]); |
|
798 | } |
||
799 | |||
800 | 1301 | if (is_array($array[$new_key])) { |
|
801 | 1301 | $array[$new_key] = $this->removeKeyPrefix($prefix, $array[$new_key]); |
|
802 | } |
||
803 | } |
||
804 | |||
805 | 1301 | return $array; |
|
806 | } |
||
807 | |||
808 | /** |
||
809 | * Processes an array of 'select' clauses |
||
810 | * |
||
811 | * @param array $options Options |
||
812 | * |
||
813 | * @return array |
||
814 | */ |
||
815 | 1301 | protected function normalizeSelectClauses(array $options = []) { |
|
816 | |||
817 | 1301 | $options = _elgg_normalize_plural_options_array($options, ['select']); |
|
818 | |||
819 | 1301 | foreach ($options['selects'] as $key => $clause) { |
|
820 | 28 | if (empty($clause)) { |
|
821 | 1 | unset($options['selects'][$key]); |
|
822 | 1 | continue; |
|
823 | } |
||
824 | |||
825 | 28 | if ($clause instanceof SelectClause) { |
|
826 | 2 | continue; |
|
827 | } |
||
828 | |||
829 | 27 | $options['selects'][$key] = new SelectClause($clause); |
|
830 | } |
||
831 | |||
832 | 1301 | return $options; |
|
833 | } |
||
834 | |||
835 | /** |
||
836 | * Processes an array of 'where' clauses |
||
837 | * |
||
838 | * @param array $options Options |
||
839 | * |
||
840 | * @return array |
||
841 | */ |
||
842 | 1301 | protected function normalizeWhereClauses(array $options = []) { |
|
843 | |||
844 | 1301 | $options = _elgg_normalize_plural_options_array($options, ['where']); |
|
845 | |||
846 | 1301 | foreach ($options['wheres'] as $key => $clause) { |
|
847 | 481 | if (empty($clause)) { |
|
848 | 1 | unset($options['wheres'][$key]); |
|
849 | 1 | continue; |
|
850 | } |
||
851 | |||
852 | 481 | if ($clause instanceof WhereClause) { |
|
853 | 3 | continue; |
|
854 | } |
||
855 | |||
856 | 481 | if (is_string($clause)) { |
|
857 | 189 | elgg_deprecated_notice(" |
|
858 | Using literal MySQL statements in 'wheres' options parameter is deprecated. |
||
859 | Instead use a closure that receives an instanceof of QueryBuilder |
||
860 | and returns a composite DBAL expression |
||
861 | |||
862 | 189 | {{ $clause }} |
|
863 | 189 | ", '3.0'); |
|
864 | } |
||
865 | |||
866 | 481 | $options['wheres'][$key] = new WhereClause($clause); |
|
867 | } |
||
868 | |||
869 | 1301 | return $options; |
|
870 | } |
||
871 | |||
872 | /** |
||
873 | * Processes an array of 'joins' clauses |
||
874 | * |
||
875 | * @param array $options Options |
||
876 | * |
||
877 | * @return array |
||
878 | * @throws \InvalidParameterException |
||
879 | */ |
||
880 | 1301 | protected function normalizeJoinClauses(array $options = []) { |
|
881 | |||
882 | 1301 | $options = _elgg_normalize_plural_options_array($options, ['join']); |
|
883 | |||
884 | 1301 | foreach ($options['joins'] as $key => $join) { |
|
885 | 7 | if (empty($join)) { |
|
886 | 1 | unset($options['joins'][$key]); |
|
887 | 1 | continue; |
|
888 | } |
||
889 | |||
890 | 7 | if ($join instanceof JoinClause) { |
|
891 | 6 | continue; |
|
892 | } |
||
893 | |||
894 | 2 | if (is_string($join)) { |
|
895 | 2 | preg_match('/((LEFT|INNER|RIGHT)\s+)?JOIN\s+(.*?)\s+((as\s+)?(.*?)\s+)ON\s+(.*)$/im', $join, $parts); |
|
896 | |||
897 | 2 | $type = !empty($parts[2]) ? strtolower($parts[2]) : 'inner'; |
|
898 | 2 | $table = $parts[3]; |
|
899 | 2 | $alias = $parts[6]; |
|
900 | 2 | $condition = preg_replace('/\r|\n/', '', $parts[7]); |
|
901 | |||
902 | 2 | $dbprefix = elgg_get_config('dbprefix'); |
|
903 | 2 | if (strpos($table, $dbprefix) === 0) { |
|
904 | 2 | $table = substr($table, strlen($dbprefix)); |
|
905 | } |
||
906 | |||
907 | 2 | elgg_deprecated_notice(" |
|
908 | Using literal MySQL statements in 'joins' options parameter is deprecated. |
||
909 | Instead use a closure that receives an instanceof of QueryBuilder and returns an instanceof of JoinClause, |
||
910 | also consider using one of the built-in methods in QueryBuilder. |
||
911 | |||
912 | 2 | {{ $join }} |
|
913 | 2 | ", '3.0'); |
|
914 | |||
915 | 2 | $clause = new JoinClause($table, $alias, $condition, $type); |
|
916 | 2 | $options['joins'][$key] = $clause; |
|
917 | } |
||
918 | } |
||
919 | |||
920 | 1301 | return $options; |
|
921 | } |
||
922 | |||
923 | /** |
||
924 | * Processes an array of 'joins' clauses |
||
925 | * |
||
926 | * @param array $options Options |
||
927 | * |
||
928 | * @return array |
||
929 | */ |
||
930 | 1301 | protected function normalizeOrderByClauses(array $options = []) { |
|
931 | |||
932 | 1301 | $order_by = $options['order_by']; |
|
933 | 1301 | $options['order_by'] = []; |
|
934 | |||
935 | 1301 | if (!empty($order_by)) { |
|
936 | 650 | if (is_string($order_by)) { |
|
937 | 483 | elgg_deprecated_notice(" |
|
938 | Using literal MySQL statements in 'order_by' options parameter is deprecated. |
||
939 | Instead use an OrderByClause or array of them. |
||
940 | |||
941 | 483 | {{ $order_by }} |
|
942 | 483 | ", '3.0'); |
|
943 | |||
944 | 483 | $orders = explode(',', $order_by); |
|
945 | 644 | } else if (is_array($order_by)) { |
|
946 | 643 | $orders = $order_by; |
|
947 | } else { |
||
948 | 1 | $orders = [$order_by]; |
|
949 | } |
||
950 | |||
951 | 650 | foreach ($orders as $order) { |
|
952 | 650 | if ($order instanceof OrderByClause) { |
|
953 | 644 | $options['order_by'][] = $order; |
|
954 | 644 | continue; |
|
955 | } |
||
956 | |||
957 | 484 | $order = trim($order); |
|
958 | 484 | if (preg_match('/(.*)(?=\s+(asc|desc))/i', $order, $parts)) { |
|
959 | 319 | $column = $parts[1]; |
|
960 | 319 | $direction = $parts[2]; |
|
961 | } else { |
||
962 | 180 | $column = $order; |
|
963 | 180 | $direction = 'ASC'; |
|
964 | } |
||
965 | |||
966 | 484 | $direction = in_array(strtoupper($direction), [ |
|
967 | 484 | 'ASC', |
|
968 | 'DESC' |
||
969 | 484 | ]) ? strtoupper($direction) : 'ASC'; |
|
970 | |||
971 | 484 | $options['order_by'][] = new OrderByClause($column, $direction); |
|
972 | } |
||
973 | } |
||
974 | |||
975 | 1301 | foreach ($options['sort_by'] as $key => $value) { |
|
976 | 8 | $clause = new EntitySortByClause(); |
|
977 | 8 | $clause->property = elgg_extract('property', $value); |
|
978 | 8 | $clause->property_type = elgg_extract('property_type', $value); |
|
979 | 8 | $clause->join_type = elgg_extract('join_type', $value, 'inner'); |
|
980 | 8 | $clause->direction = elgg_extract('direction', $value); |
|
981 | 8 | $clause->signed = elgg_extract('signed', $value); |
|
982 | |||
983 | 8 | $options['order_by'][] = $clause; |
|
984 | } |
||
985 | |||
986 | 1301 | return $options; |
|
987 | } |
||
988 | |||
989 | /** |
||
990 | * Normalize 'group_by' statements |
||
991 | * |
||
992 | * @param array $options Options |
||
993 | * |
||
994 | * @return array |
||
995 | */ |
||
996 | 1301 | protected function normalizeGroupByClauses(array $options = []) { |
|
997 | |||
998 | 1301 | if (!isset($options['having'])) { |
|
999 | 1297 | $options['having'] = []; |
|
1000 | } else { |
||
1001 | 313 | if (!is_array($options['having'])) { |
|
1002 | 2 | $options['having'] = [$options['having']]; |
|
1003 | } |
||
1004 | |||
1005 | 313 | foreach ($options['having'] as $key => $expr) { |
|
1006 | 7 | if ($expr instanceof HavingClause) { |
|
1007 | 1 | continue; |
|
1008 | } |
||
1009 | |||
1010 | 6 | $options['having'][$key] = new HavingClause($expr); |
|
1011 | } |
||
1012 | } |
||
1013 | |||
1014 | 1301 | if (empty($options['group_by'])) { |
|
1015 | 1296 | $options['group_by'] = []; |
|
1016 | } |
||
1017 | |||
1018 | 1301 | if (is_string($options['group_by'])) { |
|
1019 | 13 | $clause = $options['group_by']; |
|
1020 | |||
1021 | 13 | $options['group_by'] = explode(',', $options['group_by']); |
|
1022 | |||
1023 | 13 | if (count($options['group_by']) > 1) { |
|
1024 | 5 | elgg_deprecated_notice(" |
|
1025 | Using literal MySQL statements in 'group_by' options parameter is deprecated. |
||
1026 | Instead use a closure that receives an instanceof of QueryBuilder |
||
1027 | and returns a prepared clause. |
||
1028 | |||
1029 | 5 | {{ $clause }} |
|
1030 | 5 | ", '3.0'); |
|
1031 | } |
||
1032 | } |
||
1033 | |||
1034 | 1301 | $options['group_by'] = array_map(function ($e) { |
|
1035 | 19 | if (!$e instanceof GroupByClause) { |
|
1036 | 18 | $e = new GroupByClause(trim($e)); |
|
1037 | } |
||
1038 | |||
1039 | 19 | return $e; |
|
1040 | 1301 | }, $options['group_by']); |
|
1041 | |||
1042 | return $options; |
||
1043 | } |
||
1044 | } |
||
1045 |
This check compares calls to functions or methods with their respective definitions. If the call has less 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. Please note the @ignore annotation hint above.