metastrings.php ➔ _elgg_get_metastring_based_objects()   F
last analyzed

Complexity

Conditions 40
Paths > 20000

Size

Total Lines 266

Duplication

Lines 31
Ratio 11.65 %

Code Coverage

Tests 0
CRAP Score 1640

Importance

Changes 0
Metric Value
cc 40
nc 11308060
nop 1
dl 31
loc 266
ccs 0
cts 171
cp 0
crap 1640
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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
 * Elgg metastrngs
4
 * Functions to manage object metastrings.
5
 *
6
 * @package Elgg.Core
7
 * @subpackage DataModel.MetaStrings
8
 */
9
10
/**
11
 * Gets the metastring identifier for a value.
12
 *
13
 * Elgg normalizes the names and values of annotations and metadata. This function
14
 * provides the identifier used as the index in the metastrings table. Plugin
15
 * developers should only use this if denormalizing names/values for performance
16
 * reasons (to avoid multiple joins on the metastrings table).
17
 *
18
 * @param string $string         The value
19
 * @param bool   $case_sensitive Should the retrieval be case sensitive?
20
 *                               If not, there may be more than one result
21
 *
22
 * @return int|array metastring id or array of ids
23
 * @since 1.9.0
24
 */
25
function elgg_get_metastring_id($string, $case_sensitive = true) {
26
	return _elgg_services()->metastringsTable->getId($string, $case_sensitive);
27
}
28
29
/**
30
 * Add a metastring.
31
 *
32
 * @warning You should not call this directly. Use elgg_get_metastring_id().
33
 *
34
 * @param string $string The value to be normalized
35
 * @return int The identifier for this string
36
 */
37
function _elgg_add_metastring($string) {
38
	return _elgg_services()->metastringsTable->add($string);
39
}
40
41
/**
42
 * Returns an array of either \ElggAnnotation or \ElggMetadata objects.
43
 * Accepts all elgg_get_entities() options for entity restraints.
44
 *
45
 * @see elgg_get_entities
46
 *
47
 * @param array $options Array in format:
48
 *
49
 * 	metastring_names              => null|ARR metastring names
50
 *
51
 * 	metastring_values             => null|ARR metastring values
52
 *
53
 * 	metastring_ids                => null|ARR metastring ids
54
 *
55
 * 	metastring_case_sensitive     => BOOL     Overall Case sensitive
56
 *
57
 *  metastring_owner_guids        => null|ARR Guids for metadata owners
58
 *
59
 *  metastring_created_time_lower => INT      Lower limit for created time.
60
 *
61
 *  metastring_created_time_upper => INT      Upper limit for created time.
62
 *
63
 *  metastring_calculation        => STR      Perform the MySQL function on the metastring values
64
 *                                            returned.
65
 *                                            This differs from egef_annotation_calculation in that
66
 *                                            it returns only the calculation of all annotation values.
67
 *                                            You can sum, avg, count, etc. egef_annotation_calculation()
68
 *                                            returns \ElggEntities ordered by a calculation on their
69
 *                                            annotation values.
70
 *
71
 *  metastring_type               => STR      metadata or annotation(s)
72
 *
73
 * @return \ElggExtender[]|int An array or count of metastring based objects
74
 * @access private
75
 */
76
function _elgg_get_metastring_based_objects($options) {
77
	$options = _elgg_normalize_metastrings_options($options);
78
79
	switch ($options['metastring_type']) {
80
		case 'metadata':
81
			$type = 'metadata';
82
			$callback = 'row_to_elggmetadata';
83
			break;
84
85
		case 'annotations':
86
		case 'annotation':
87
			$type = 'annotations';
88
			$callback = 'row_to_elggannotation';
89
			break;
90
91
		default:
92
			return false;
93
	}
94
95
	$defaults = array(
96
		// entities
97
		'types' => ELGG_ENTITIES_ANY_VALUE,
98
		'subtypes' => ELGG_ENTITIES_ANY_VALUE,
99
		'type_subtype_pairs' => ELGG_ENTITIES_ANY_VALUE,
100
101
		'guids' => ELGG_ENTITIES_ANY_VALUE,
102
		'owner_guids' => ELGG_ENTITIES_ANY_VALUE,
103
		'container_guids' => ELGG_ENTITIES_ANY_VALUE,
104
		'site_guids' => get_config('site_guid'),
105
106
		'modified_time_lower' => ELGG_ENTITIES_ANY_VALUE,
107
		'modified_time_upper' => ELGG_ENTITIES_ANY_VALUE,
108
		'created_time_lower' => ELGG_ENTITIES_ANY_VALUE,
109
		'created_time_upper' => ELGG_ENTITIES_ANY_VALUE,
110
111
		// options are normalized to the plural in case we ever add support for them.
112
		'metastring_names' => ELGG_ENTITIES_ANY_VALUE,
113
		'metastring_values' => ELGG_ENTITIES_ANY_VALUE,
114
		//'metastring_name_value_pairs' => ELGG_ENTITIES_ANY_VALUE,
115
		//'metastring_name_value_pairs_operator' => 'AND',
116
117
		'metastring_case_sensitive' => true,
118
		//'order_by_metastring' => array(),
119
		'metastring_calculation' => ELGG_ENTITIES_NO_VALUE,
120
121
		'metastring_created_time_lower' => ELGG_ENTITIES_ANY_VALUE,
122
		'metastring_created_time_upper' => ELGG_ENTITIES_ANY_VALUE,
123
124
		'metastring_owner_guids' => ELGG_ENTITIES_ANY_VALUE,
125
126
		'metastring_ids' => ELGG_ENTITIES_ANY_VALUE,
127
128
		// sql
129
		'order_by' => 'n_table.time_created ASC, n_table.id ASC',
130
		'limit' => elgg_get_config('default_limit'),
131
		'offset' => 0,
132
		'count' => false,
133
		'selects' => array(),
134
		'wheres' => array(),
135
		'joins' => array(),
136
137
		'distinct' => true,
138
		'preload_owners' => false,
139
		'callback' => $callback,
140
	);
141
142
	// @todo Ignore site_guid right now because of #2910
143
	$options['site_guid'] = ELGG_ENTITIES_ANY_VALUE;
144
145
	$options = array_merge($defaults, $options);
146
147
	// can't use helper function with type_subtype_pair because
148
	// it's already an array...just need to merge it
149 View Code Duplication
	if (isset($options['type_subtype_pair'])) {
150
		if (isset($options['type_subtype_pairs'])) {
151
			$options['type_subtype_pairs'] = array_merge($options['type_subtype_pairs'],
152
				$options['type_subtype_pair']);
153
		} else {
154
			$options['type_subtype_pairs'] = $options['type_subtype_pair'];
155
		}
156
	}
157
158
	$singulars = array(
159
		'type', 'subtype', 'type_subtype_pair',
160
		'guid', 'owner_guid', 'container_guid', 'site_guid',
161
		'metastring_name', 'metastring_value',
162
		'metastring_owner_guid', 'metastring_id',
163
		'select', 'where', 'join'
164
	);
165
166
	$options = _elgg_normalize_plural_options_array($options, $singulars);
167
168
	if (!$options) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $options of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
169
		return false;
170
	}
171
172
	$db_prefix = elgg_get_config('dbprefix');
173
174
	// evaluate where clauses
175
	if (!is_array($options['wheres'])) {
176
		$options['wheres'] = array($options['wheres']);
177
	}
178
179
	$wheres = $options['wheres'];
180
181
	// entities
182
	$wheres[] = _elgg_get_entity_type_subtype_where_sql('e', $options['types'],
183
		$options['subtypes'], $options['type_subtype_pairs']);
184
185
	$wheres[] = _elgg_get_guid_based_where_sql('e.guid', $options['guids']);
186
	$wheres[] = _elgg_get_guid_based_where_sql('e.owner_guid', $options['owner_guids']);
187
	$wheres[] = _elgg_get_guid_based_where_sql('e.container_guid', $options['container_guids']);
188
	$wheres[] = _elgg_get_guid_based_where_sql('e.site_guid', $options['site_guids']);
189
190
	$wheres[] = _elgg_get_entity_time_where_sql('e', $options['created_time_upper'],
191
		$options['created_time_lower'], $options['modified_time_upper'], $options['modified_time_lower']);
192
193
194
	$wheres[] = _elgg_get_entity_time_where_sql('n_table', $options['metastring_created_time_upper'],
195
		$options['metastring_created_time_lower'], null, null);
196
197
	$wheres[] = _elgg_get_guid_based_where_sql('n_table.owner_guid',
198
		$options['metastring_owner_guids']);
199
200
	// see if any functions failed
201
	// remove empty strings on successful functions
202 View Code Duplication
	foreach ($wheres as $i => $where) {
203
		if ($where === false) {
204
			return false;
205
		} elseif (empty($where)) {
206
			unset($wheres[$i]);
207
		}
208
	}
209
210
	// remove identical where clauses
211
	$wheres = array_unique($wheres);
212
213
	// evaluate join clauses
214
	if (!is_array($options['joins'])) {
215
		$options['joins'] = array($options['joins']);
216
	}
217
218
	$joins = array();
219
	$joins[] = "JOIN {$db_prefix}entities e ON n_table.entity_guid = e.guid";
220
221
	// evaluate selects
222
	if (!is_array($options['selects'])) {
223
		$options['selects'] = array($options['selects']);
224
	}
225
226
	$selects = $options['selects'];
227
228
	// For performance reasons we don't want the joins required for metadata / annotations
229
	// unless we're going through one of their callbacks.
230
	// this means we expect the functions passing different callbacks to pass their required joins.
231
	// If we're doing a calculation
232
	$custom_callback = ($options['callback'] == 'row_to_elggmetadata'
233
						|| $options['callback'] == 'row_to_elggannotation');
234
	$is_calculation = $options['metastring_calculation'] ? true : false;
235
236
	if ($custom_callback || $is_calculation) {
237
		$joins[] = "JOIN {$db_prefix}metastrings n on n_table.name_id = n.id";
238
		$joins[] = "JOIN {$db_prefix}metastrings v on n_table.value_id = v.id";
239
240
		$selects[] = 'n.string as name';
241
		$selects[] = 'v.string as value';
242
	}
243
244
	// add optional joins
245
	$joins = array_merge($joins, $options['joins']);
246
247
	// metastrings
248
	$metastring_clauses = _elgg_get_metastring_sql('n_table', $options['metastring_names'],
249
		$options['metastring_values'], null, $options['metastring_ids'],
250
		$options['metastring_case_sensitive']);
251
252
	if ($metastring_clauses) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $metastring_clauses of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
253
		$wheres = array_merge($wheres, $metastring_clauses['wheres']);
254
		$joins = array_merge($joins, $metastring_clauses['joins']);
255
	} else {
256
		$wheres[] = _elgg_get_access_where_sql(array(
257
			'table_alias' => 'n_table',
258
			'guid_column' => 'entity_guid',
259
		));
260
	}
261
262
	$distinct = $options['distinct'] ? "DISTINCT " : "";
263
264
	if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE && !$options['count']) {
265
		$selects = array_unique($selects);
266
		// evalutate selects
267
		$select_str = '';
268
		if ($selects) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $selects of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
269
			foreach ($selects as $select) {
270
				$select_str .= ", $select";
271
			}
272
		}
273
274
		$query = "SELECT $distinct n_table.*{$select_str} FROM {$db_prefix}$type n_table";
275
	} elseif ($options['count']) {
276
		// count is over the entities
277
		$query = "SELECT count($distinct e.guid) as calculation FROM {$db_prefix}$type n_table";
278
	} else {
279
		$query = "SELECT {$options['metastring_calculation']}(v.string) as calculation FROM {$db_prefix}$type n_table";
280
	}
281
282 View Code Duplication
	foreach ($joins as $i => $join) {
283
		if ($join === false) {
284
			return false;
285
		} elseif (empty($join)) {
286
			unset($joins[$i]);
287
		}
288
	}
289
290
	// remove identical join clauses
291
	$joins = array_unique($joins);
292
293
	// add joins
294
	foreach ($joins as $j) {
295
		$query .= " $j ";
296
	}
297
298
	// add wheres
299
	$query .= ' WHERE ';
300
301
	foreach ($wheres as $w) {
302
		$query .= " $w AND ";
303
	}
304
305
	// Add access controls
306
	$query .= _elgg_get_access_where_sql(array('table_alias' => 'e'));
307
308
	// reverse order by
309
	if (isset($options['reverse_order_by']) && $options['reverse_order_by']) {
310
		$options['order_by'] = _elgg_sql_reverse_order_by_clause($options['order_by']);
311
	}
312
313
	if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE && !$options['count']) {
314
		if (isset($options['group_by'])) {
315
			$options['group_by'] = sanitise_string($options['group_by']);
316
			$query .= " GROUP BY {$options['group_by']}";
317
		}
318
319 View Code Duplication
		if (isset($options['order_by']) && $options['order_by']) {
320
			$options['order_by'] = sanitise_string($options['order_by']);
321
			$query .= " ORDER BY {$options['order_by']}, n_table.id";
322
		}
323
324 View Code Duplication
		if ($options['limit']) {
325
			$limit = sanitise_int($options['limit']);
326
			$offset = sanitise_int($options['offset'], false);
327
			$query .= " LIMIT $offset, $limit";
328
		}
329
330
		$dt = get_data($query, $options['callback']);
331
332
		if ($options['preload_owners'] && is_array($dt) && count($dt) > 1) {
333
			_elgg_services()->entityPreloader->preload($dt, ['owner_guid']);
334
		}
335
336
		return $dt;
337
	} else {
338
		$result = get_data_row($query);
339
		return $result->calculation;
340
	}
341
}
342
343
/**
344
 * Returns an array of joins and wheres for use in metastrings.
345
 *
346
 * @note The $pairs is reserved for name/value pairs if we want to implement those.
347
 *
348
 * @param string $table          The annotation or metadata table name or alias
349
 * @param array  $names          An array of names
350
 * @param array  $values         An array of values
351
 * @param array  $pairs          Name / value pairs. Not currently used.
352
 * @param array  $ids            Metastring IDs
353
 * @param bool   $case_sensitive Should name and values be case sensitive?
354
 *
355
 * @return array
356
 * @access private
357
 */
358
function _elgg_get_metastring_sql($table, $names = null, $values = null,
359
	$pairs = null, $ids = null, $case_sensitive = false) {
360
361
	if ((!$names && $names !== 0)
362
		&& (!$values && $values !== 0)
363
		&& !$ids
364
		&& (!$pairs && $pairs !== 0)) {
365
366
		return array();
367
	}
368
369
	$db_prefix = elgg_get_config('dbprefix');
370
371
	// binary forces byte-to-byte comparision of strings, making
372
	// it case- and diacritical-mark- sensitive.
373
	// only supported on values.
374
	$binary = ($case_sensitive) ? ' BINARY ' : '';
375
376
	$return = array (
377
		'joins' => array (),
378
		'wheres' => array()
379
	);
380
381
	$wheres = array();
382
383
	// get names wheres and joins
384
	$names_where = '';
385 View Code Duplication
	if ($names !== null) {
386
		if (!is_array($names)) {
387
			$names = array($names);
388
		}
389
390
		$sanitised_names = array();
391
		foreach ($names as $name) {
392
			// normalise to 0.
393
			if (!$name) {
394
				$name = '0';
395
			}
396
			$sanitised_names[] = '\'' . sanitise_string($name) . '\'';
397
		}
398
399
		if ($names_str = implode(',', $sanitised_names)) {
400
			$return['joins'][] = "JOIN {$db_prefix}metastrings msn on $table.name_id = msn.id";
401
			$names_where = "(msn.string IN ($names_str))";
402
		}
403
	}
404
405
	// get values wheres and joins
406
	$values_where = '';
407 View Code Duplication
	if ($values !== null) {
408
		if (!is_array($values)) {
409
			$values = array($values);
410
		}
411
412
		$sanitised_values = array();
413
		foreach ($values as $value) {
414
			// normalize to 0
415
			if (!$value) {
416
				$value = 0;
417
			}
418
			$sanitised_values[] = '\'' . sanitise_string($value) . '\'';
419
		}
420
421
		if ($values_str = implode(',', $sanitised_values)) {
422
			$return['joins'][] = "JOIN {$db_prefix}metastrings msv on $table.value_id = msv.id";
423
			$values_where = "({$binary}msv.string IN ($values_str))";
424
		}
425
	}
426
427
	if ($ids !== null) {
428
		if (!is_array($ids)) {
429
			$ids = array($ids);
430
		}
431
432
		$ids_str = implode(',', $ids);
433
434
		if ($ids_str) {
435
			$wheres[] = "n_table.id IN ($ids_str)";
436
		}
437
	}
438
439 View Code Duplication
	if ($names_where && $values_where) {
440
		$wheres[] = "($names_where AND $values_where)";
441
	} elseif ($names_where) {
442
		$wheres[] = $names_where;
443
	} elseif ($values_where) {
444
		$wheres[] = $values_where;
445
	}
446
447
	$wheres[] = _elgg_get_access_where_sql(array(
448
		'table_alias' => $table,
449
		'guid_column' => 'entity_guid',
450
	));
451
452
	if ($where = implode(' AND ', $wheres)) {
453
		$return['wheres'][] = "($where)";
454
	}
455
456
	return $return;
457
}
458
459
/**
460
 * Normalizes metadata / annotation option names to their corresponding metastrings name.
461
 *
462
 * @param array $options An options array
463
 * @return array
464
 * @access private
465
 */
466
function _elgg_normalize_metastrings_options(array $options = array()) {
467
468
	// support either metastrings_type or metastring_type
469
	// because I've made this mistake many times and hunting it down is a pain...
470
	$type = elgg_extract('metastring_type', $options, null);
471
	$type = elgg_extract('metastrings_type', $options, $type);
472
473
	$options['metastring_type'] = $type;
474
475
	// support annotation_ and annotations_ because they're way too easy to confuse
476
	$prefixes = array('metadata_', 'annotation_', 'annotations_');
477
478
	// map the metadata_* options to metastring_* options
479
	$map = array(
480
		'names'					=>	'metastring_names',
481
		'values'				=>	'metastring_values',
482
		'case_sensitive'		=>	'metastring_case_sensitive',
483
		'owner_guids'			=>	'metastring_owner_guids',
484
		'created_time_lower'	=>	'metastring_created_time_lower',
485
		'created_time_upper'	=>	'metastring_created_time_upper',
486
		'calculation'			=>	'metastring_calculation',
487
		'ids'					=>	'metastring_ids',
488
	);
489
490
	foreach ($prefixes as $prefix) {
491
		$singulars = array("{$prefix}name", "{$prefix}value", "{$prefix}owner_guid", "{$prefix}id");
492
		$options = _elgg_normalize_plural_options_array($options, $singulars);
493
494
		foreach ($map as $specific => $normalized) {
495
			$key = $prefix . $specific;
496
			if (isset($options[$key])) {
497
				$options[$normalized] = $options[$key];
498
			}
499
		}
500
	}
501
502
	return $options;
503
}
504
505
/**
506
 * Enables or disables a metastrings-based object by its id.
507
 *
508
 * @warning To enable disabled metastrings you must first use
509
 * {@link access_show_hidden_entities()}.
510
 *
511
 * @param int    $id      The object's ID
512
 * @param string $enabled Value to set to: yes or no
513
 * @param string $type    Metastring type: metadata or annotation
514
 *
515
 * @return bool
516
 * @throws InvalidParameterException
517
 * @access private
518
 */
519
function _elgg_set_metastring_based_object_enabled_by_id($id, $enabled, $type) {
520
	$id = (int)$id;
521
	$db_prefix = elgg_get_config('dbprefix');
522
523
	$object = _elgg_get_metastring_based_object_from_id($id, $type);
524
525
	switch ($type) {
526
		case 'annotation':
527
		case 'annotations':
528
			$type = 'annotation';
529
			$table = "{$db_prefix}annotations";
530
			break;
531
532
		case 'metadata':
533
			$table = "{$db_prefix}metadata";
534
			break;
535
	}
536
537
	if ($enabled === 'yes' || $enabled === 1 || $enabled === true) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $enabled (string) and 1 (integer) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
538
		$enabled = 'yes';
539
		$event = 'enable';
540
	} elseif ($enabled === 'no' || $enabled === 0 || $enabled === false) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $enabled (string) and 0 (integer) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
541
		$enabled = 'no';
542
		$event = 'disable';
543
	} else {
544
		return false;
545
	}
546
547
	$return = false;
548
549
	if ($object) {
550
		// don't set it if it's already set.
551
		if ($object->enabled == $enabled) {
552
			$return = false;
553
		} elseif ($object->canEdit() && (elgg_trigger_event($event, $type, $object))) {
0 ignored issues
show
Documentation introduced by
$object is of type object<ElggExtender>, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
554
			$return = update_data("UPDATE $table SET enabled = '$enabled' where id = $id");
0 ignored issues
show
Bug introduced by
The variable $table does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
555
		}
556
	}
557
558
	return $return;
559
}
560
561
/**
562
 * Runs metastrings-based objects found using $options through $callback
563
 *
564
 * @warning Unlike _elgg_get_metastring_based_objects() this will not accept an
565
 * empty options array!
566
 *
567
 * @warning This returns null on no ops.
568
 *
569
 * @param array  $options    An options array. {@link _elgg_get_metastring_based_objects()}
570
 * @param string $callback   The callback to pass each result through
571
 * @param bool   $inc_offset Increment the offset? Pass false for callbacks that delete / disable
572
 *
573
 * @return bool|null true on success, false on failure, null if no objects are found.
574
 * @access private
575
 */
576
function _elgg_batch_metastring_based_objects(array $options, $callback, $inc_offset = true) {
577
	if (!$options || !is_array($options)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $options of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
578
		return false;
579
	}
580
581
	$batch = new \ElggBatch('_elgg_get_metastring_based_objects', $options, $callback, 50, $inc_offset);
582
	return $batch->callbackResult;
583
}
584
585
/**
586
 * Returns a singular metastring-based object by its ID.
587
 *
588
 * @param int    $id   The metastring-based object's ID
589
 * @param string $type The type: annotation or metadata
590
 * @return \ElggExtender
591
 * @access private
592
 */
593
function _elgg_get_metastring_based_object_from_id($id, $type) {
594
	$id = (int)$id;
595
	if (!$id) {
596
		return false;
597
	}
598
599
	$options = array(
600
		'metastring_type' => $type,
601
		'metastring_id' => $id,
602
	);
603
604
	$obj = _elgg_get_metastring_based_objects($options);
605
606
	if ($obj && count($obj) == 1) {
607
		return $obj[0];
608
	}
609
610
	return false;
611
}
612
613
/**
614
 * Deletes a metastring-based object by its id
615
 *
616
 * @param int    $id   The object's ID
617
 * @param string $type The object's metastring type: annotation or metadata
618
 * @return bool
619
 * @access private
620
 */
621
function _elgg_delete_metastring_based_object_by_id($id, $type) {
622
	$id = (int)$id;
623
	$db_prefix = elgg_get_config('dbprefix');
624
625
	switch ($type) {
626
		case 'annotations':
627
		case 'annotation':
628
			$table = $db_prefix . 'annotations';
629
			$type = 'annotation';
630
			break;
631
632
		case 'metadata':
633
			$table = $db_prefix . 'metadata';
634
			$type = 'metadata';
635
			break;
636
637
		default:
638
			return false;
639
	}
640
641
	$obj = _elgg_get_metastring_based_object_from_id($id, $type);
642
643
	if ($obj) {
644
		// Tidy up if memcache is enabled.
645
		// @todo only metadata is supported
646
		if ($type == 'metadata') {
647
			static $metabyname_memcache;
648
			if ((!$metabyname_memcache) && (is_memcache_available())) {
649
				$metabyname_memcache = new \ElggMemcache('metabyname_memcache');
650
			}
651
652
			if ($metabyname_memcache) {
653
				// @todo why name_id? is that even populated?
654
				$metabyname_memcache->delete("{$obj->entity_guid}:{$obj->name_id}");
655
			}
656
		}
657
658
		if ($obj->canEdit()) {
659
			// bc code for when we triggered 'delete', 'annotations' #4770
660
			$result = true;
661
			if ($type == "annotation") {
662
				$result = elgg_trigger_event('delete', 'annotations', $obj);
0 ignored issues
show
Documentation introduced by
$obj is of type object<ElggExtender>, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
663
				if ($result === false) {
664
					elgg_deprecated_notice("Use the event 'delete', 'annotation'", 1.9);
665
				}
666
			}
667
668
			if (elgg_trigger_event('delete', $type, $obj) && $result) {
0 ignored issues
show
Documentation introduced by
$obj is of type object<ElggExtender>, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
669
				return (bool)delete_data("DELETE FROM $table WHERE id = $id");
670
			}
671
		}
672
	}
673
674
	return false;
675
}
676
677
/**
678
 * Returns options to pass to elgg_get_entities() for metastrings operations.
679
 *
680
 * @param string $type    Metastring type: annotation or metadata
681
 * @param array  $options Options
682
 *
683
 * @return array
684
 * @access private
685
 */
686
function _elgg_entities_get_metastrings_options($type, $options) {
687
	$valid_types = array('metadata', 'annotation');
688
	if (!in_array($type, $valid_types)) {
689
		return false;
690
	}
691
692
	// the options for annotations are singular (annotation_name) but the table
693
	// is plural (elgg_annotations) so rewrite for the table name.
694
	$n_table = ($type == 'annotation') ? 'annotations' : $type;
695
696
	$singulars = array("{$type}_name", "{$type}_value",
697
		"{$type}_name_value_pair", "{$type}_owner_guid");
698
	$options = _elgg_normalize_plural_options_array($options, $singulars);
699
700
	$clauses = _elgg_get_entity_metadata_where_sql('e', $n_table, $options["{$type}_names"],
701
		$options["{$type}_values"], $options["{$type}_name_value_pairs"],
702
		$options["{$type}_name_value_pairs_operator"], $options["{$type}_case_sensitive"],
703
		$options["order_by_{$type}"], $options["{$type}_owner_guids"]);
704
705
	if ($clauses) {
706
		// merge wheres to pass to elgg_get_entities()
707
		if (isset($options['wheres']) && !is_array($options['wheres'])) {
708
			$options['wheres'] = array($options['wheres']);
709
		} elseif (!isset($options['wheres'])) {
710
			$options['wheres'] = array();
711
		}
712
713
		$options['wheres'] = array_merge($options['wheres'], $clauses['wheres']);
714
715
		// merge joins to pass to elgg_get_entities()
716
		if (isset($options['joins']) && !is_array($options['joins'])) {
717
			$options['joins'] = array($options['joins']);
718
		} elseif (!isset($options['joins'])) {
719
			$options['joins'] = array();
720
		}
721
722
		$options['joins'] = array_merge($options['joins'], $clauses['joins']);
723
724
		if ($clauses['orders']) {
725
			$order_by_metadata = implode(", ", $clauses['orders']);
726 View Code Duplication
			if (isset($options['order_by']) && $options['order_by']) {
727
				$options['order_by'] = "$order_by_metadata, {$options['order_by']}";
728
			} else {
729
				$options['order_by'] = "$order_by_metadata, e.time_created DESC";
730
			}
731
		}
732
	}
733
734
	return $options;
735
}
736
737
/**
738
 * Metastring unit tests
739
 *
740
 * @param string $hook  unit_test
741
 * @param string $type  system
742
 * @param array  $value Array of other tests
743
 *
744
 * @return array
745
 * @access private
746
 */
747
function _elgg_metastrings_test($hook, $type, $value) {
748
	global $CONFIG;
749
	$value[] = $CONFIG->path . 'engine/tests/ElggCoreMetastringsTest.php';
750
	return $value;
751
}
752
753
return function(\Elgg\EventsService $events, \Elgg\HooksRegistrationService $hooks) {
754
	$hooks->registerHandler('unit_test', 'system', '_elgg_metastrings_test');
755
};
756